1 // 4.0.25 (2014-04-30)
  2 
  3 /**
  4  * Compiled inline version. (Library mode)
  5  */
  6 
  7 /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
  8 /*globals $code */
  9 
 10 (function(exports, undefined) {
 11 	"use strict";
 12 
 13 	var modules = {};
 14 
 15 	function require(ids, callback) {
 16 		var module, defs = [];
 17 
 18 		for (var i = 0; i < ids.length; ++i) {
 19 			module = modules[ids[i]] || resolve(ids[i]);
 20 			if (!module) {
 21 				throw 'module definition dependecy not found: ' + ids[i];
 22 			}
 23 
 24 			defs.push(module);
 25 		}
 26 
 27 		callback.apply(null, defs);
 28 	}
 29 
 30 	function define(id, dependencies, definition) {
 31 		if (typeof id !== 'string') {
 32 			throw 'invalid module definition, module id must be defined and be a string';
 33 		}
 34 
 35 		if (dependencies === undefined) {
 36 			throw 'invalid module definition, dependencies must be specified';
 37 		}
 38 
 39 		if (definition === undefined) {
 40 			throw 'invalid module definition, definition function must be specified';
 41 		}
 42 
 43 		require(dependencies, function() {
 44 			modules[id] = definition.apply(null, arguments);
 45 		});
 46 	}
 47 
 48 	function defined(id) {
 49 		return !!modules[id];
 50 	}
 51 
 52 	function resolve(id) {
 53 		var target = exports;
 54 		var fragments = id.split(/[.\/]/);
 55 
 56 		for (var fi = 0; fi < fragments.length; ++fi) {
 57 			if (!target[fragments[fi]]) {
 58 				return;
 59 			}
 60 
 61 			target = target[fragments[fi]];
 62 		}
 63 
 64 		return target;
 65 	}
 66 
 67 	function expose(ids) {
 68 		for (var i = 0; i < ids.length; i++) {
 69 			var target = exports;
 70 			var id = ids[i];
 71 			var fragments = id.split(/[.\/]/);
 72 
 73 			for (var fi = 0; fi < fragments.length - 1; ++fi) {
 74 				if (target[fragments[fi]] === undefined) {
 75 					target[fragments[fi]] = {};
 76 				}
 77 
 78 				target = target[fragments[fi]];
 79 			}
 80 
 81 			target[fragments[fragments.length - 1]] = modules[id];
 82 		}
 83 	}
 84 
 85 // Included from: js/tinymce/classes/dom/Sizzle.jQuery.js
 86 
 87 /**
 88  * Sizzle.jQuery.js
 89  *
 90  * Copyright, Moxiecode Systems AB
 91  * Released under LGPL License.
 92  *
 93  * License: http://www.tinymce.com/license
 94  * Contributing: http://www.tinymce.com/contributing
 95  */
 96 
 97 /*global jQuery:true */
 98 
 99 /*
100  * Fake Sizzle using jQuery.
101  */
102 define("tinymce/dom/Sizzle", [], function() {
103 	// Detect if jQuery is loaded
104 	if (!window.jQuery) {
105 		throw new Error("Load jQuery first");
106 	}
107 
108 	return jQuery.find;
109 });
110 
111 // Included from: js/tinymce/classes/html/Styles.js
112 
113 /**
114  * Styles.js
115  *
116  * Copyright, Moxiecode Systems AB
117  * Released under LGPL License.
118  *
119  * License: http://www.tinymce.com/license
120  * Contributing: http://www.tinymce.com/contributing
121  */
122 
123 /**
124  * This class is used to parse CSS styles it also compresses styles to reduce the output size.
125  *
126  * @example
127  * var Styles = new tinymce.html.Styles({
128  *    url_converter: function(url) {
129  *       return url;
130  *    }
131  * });
132  *
133  * styles = Styles.parse('border: 1px solid red');
134  * styles.color = 'red';
135  *
136  * console.log(new tinymce.html.StyleSerializer().serialize(styles));
137  *
138  * @class tinymce.html.Styles
139  * @version 3.4
140  */
141 define("tinymce/html/Styles", [], function() {
142 	return function(settings, schema) {
143 		/*jshint maxlen:255 */
144 		/*eslint max-len:0 */
145 		var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
146 			urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
147 			styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
148 			trimRightRegExp = /\s+$/,
149 			undef, i, encodingLookup = {}, encodingItems, invisibleChar = '\uFEFF';
150 
151 		settings = settings || {};
152 
153 		encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
154 		for (i = 0; i < encodingItems.length; i++) {
155 			encodingLookup[encodingItems[i]] = invisibleChar + i;
156 			encodingLookup[invisibleChar + i] = encodingItems[i];
157 		}
158 
159 		function toHex(match, r, g, b) {
160 			function hex(val) {
161 				val = parseInt(val, 10).toString(16);
162 
163 				return val.length > 1 ? val : '0' + val; // 0 -> 00
164 			}
165 
166 			return '#' + hex(r) + hex(g) + hex(b);
167 		}
168 
169 		return {
170 			/**
171 			 * Parses the specified RGB color value and returns a hex version of that color.
172 			 *
173 			 * @method toHex
174 			 * @param {String} color RGB string value like rgb(1,2,3)
175 			 * @return {String} Hex version of that RGB value like #FF00FF.
176 			 */
177 			toHex: function(color) {
178 				return color.replace(rgbRegExp, toHex);
179 			},
180 
181 			/**
182 			 * Parses the specified style value into an object collection. This parser will also
183 			 * merge and remove any redundant items that browsers might have added. It will also convert non hex
184 			 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
185 			 *
186 			 * @method parse
187 			 * @param {String} css Style value to parse for example: border:1px solid red;.
188 			 * @return {Object} Object representation of that style like {border: '1px solid red'}
189 			 */
190 			parse: function(css) {
191 				var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
192 				var urlConverterScope = settings.url_converter_scope || this;
193 
194 				function compress(prefix, suffix, noJoin) {
195 					var top, right, bottom, left;
196 
197 					top = styles[prefix + '-top' + suffix];
198 					if (!top) {
199 						return;
200 					}
201 
202 					right = styles[prefix + '-right' + suffix];
203 					if (!right) {
204 						return;
205 					}
206 
207 					bottom = styles[prefix + '-bottom' + suffix];
208 					if (!bottom) {
209 						return;
210 					}
211 
212 					left = styles[prefix + '-left' + suffix];
213 					if (!left) {
214 						return;
215 					}
216 
217 					var box = [top, right, bottom, left];
218 					i = box.length - 1;
219 					while (i--) {
220 						if (box[i] !== box[i + 1]) {
221 							break;
222 						}
223 					}
224 
225 					if (i > -1 && noJoin) {
226 						return;
227 					}
228 
229 					styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
230 					delete styles[prefix + '-top' + suffix];
231 					delete styles[prefix + '-right' + suffix];
232 					delete styles[prefix + '-bottom' + suffix];
233 					delete styles[prefix + '-left' + suffix];
234 				}
235 
236 				/**
237 				 * Checks if the specific style can be compressed in other words if all border-width are equal.
238 				 */
239 				function canCompress(key) {
240 					var value = styles[key], i;
241 
242 					if (!value) {
243 						return;
244 					}
245 
246 					value = value.split(' ');
247 					i = value.length;
248 					while (i--) {
249 						if (value[i] !== value[0]) {
250 							return false;
251 						}
252 					}
253 
254 					styles[key] = value[0];
255 
256 					return true;
257 				}
258 
259 				/**
260 				 * Compresses multiple styles into one style.
261 				 */
262 				function compress2(target, a, b, c) {
263 					if (!canCompress(a)) {
264 						return;
265 					}
266 
267 					if (!canCompress(b)) {
268 						return;
269 					}
270 
271 					if (!canCompress(c)) {
272 						return;
273 					}
274 
275 					// Compress
276 					styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
277 					delete styles[a];
278 					delete styles[b];
279 					delete styles[c];
280 				}
281 
282 				// Encodes the specified string by replacing all \" \' ; : with _<num>
283 				function encode(str) {
284 					isEncoded = true;
285 
286 					return encodingLookup[str];
287 				}
288 
289 				// Decodes the specified string by replacing all _<num> with it's original value \" \' etc
290 				// It will also decode the \" \' if keep_slashes is set to fale or omitted
291 				function decode(str, keep_slashes) {
292 					if (isEncoded) {
293 						str = str.replace(/\uFEFF[0-9]/g, function(str) {
294 							return encodingLookup[str];
295 						});
296 					}
297 
298 					if (!keep_slashes) {
299 						str = str.replace(/\\([\'\";:])/g, "$1");
300 					}
301 
302 					return str;
303 				}
304 
305 				function processUrl(match, url, url2, url3, str, str2) {
306 					str = str || str2;
307 
308 					if (str) {
309 						str = decode(str);
310 
311 						// Force strings into single quote format
312 						return "'" + str.replace(/\'/g, "\\'") + "'";
313 					}
314 
315 					url = decode(url || url2 || url3);
316 
317 					if (!settings.allow_script_urls) {
318 						var scriptUrl = url.replace(/[\s\r\n]+/, '');
319 
320 						if (/(java|vb)script:/i.test(scriptUrl)) {
321 							return "";
322 						}
323 
324 						if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) {
325 							return "";
326 						}
327 					}
328 
329 					// Convert the URL to relative/absolute depending on config
330 					if (urlConverter) {
331 						url = urlConverter.call(urlConverterScope, url, 'style');
332 					}
333 
334 					// Output new URL format
335 					return "url('" + url.replace(/\'/g, "\\'") + "')";
336 				}
337 
338 				if (css) {
339 					css = css.replace(/[\u0000-\u001F]/g, '');
340 
341 					// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
342 					css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
343 						return str.replace(/[;:]/g, encode);
344 					});
345 
346 					// Parse styles
347 					while ((matches = styleRegExp.exec(css))) {
348 						name = matches[1].replace(trimRightRegExp, '').toLowerCase();
349 						value = matches[2].replace(trimRightRegExp, '');
350 
351 						// Decode escaped sequences like \65 -> e
352 						/*jshint loopfunc:true*/
353 						/*eslint no-loop-func:0 */
354 						value = value.replace(/\\[0-9a-f]+/g, function(e) {
355 							return String.fromCharCode(parseInt(e.substr(1), 16));
356 						});
357 
358 						if (name && value.length > 0) {
359 							// Don't allow behavior name or expression/comments within the values
360 							if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) {
361 								continue;
362 							}
363 
364 							// Opera will produce 700 instead of bold in their style values
365 							if (name === 'font-weight' && value === '700') {
366 								value = 'bold';
367 							} else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
368 								value = value.toLowerCase();
369 							}
370 
371 							// Convert RGB colors to HEX
372 							value = value.replace(rgbRegExp, toHex);
373 
374 							// Convert URLs and force them into url('value') format
375 							value = value.replace(urlOrStrRegExp, processUrl);
376 							styles[name] = isEncoded ? decode(value, true) : value;
377 						}
378 
379 						styleRegExp.lastIndex = matches.index + matches[0].length;
380 					}
381 					// Compress the styles to reduce it's size for example IE will expand styles
382 					compress("border", "", true);
383 					compress("border", "-width");
384 					compress("border", "-color");
385 					compress("border", "-style");
386 					compress("padding", "");
387 					compress("margin", "");
388 					compress2('border', 'border-width', 'border-style', 'border-color');
389 
390 					// Remove pointless border, IE produces these
391 					if (styles.border === 'medium none') {
392 						delete styles.border;
393 					}
394 
395 					// IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
396 					// So lets asume it shouldn't be there
397 					if (styles['border-image'] === 'none') {
398 						delete styles['border-image'];
399 					}
400 				}
401 
402 				return styles;
403 			},
404 
405 			/**
406 			 * Serializes the specified style object into a string.
407 			 *
408 			 * @method serialize
409 			 * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
410 			 * @param {String} element_name Optional element name, if specified only the styles that matches the schema will be serialized.
411 			 * @return {String} String representation of the style object for example: border: 1px solid red.
412 			 */
413 			serialize: function(styles, element_name) {
414 				var css = '', name, value;
415 
416 				function serializeStyles(name) {
417 					var styleList, i, l, value;
418 
419 					styleList = schema.styles[name];
420 					if (styleList) {
421 						for (i = 0, l = styleList.length; i < l; i++) {
422 							name = styleList[i];
423 							value = styles[name];
424 
425 							if (value !== undef && value.length > 0) {
426 								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
427 							}
428 						}
429 					}
430 				}
431 
432 				// Serialize styles according to schema
433 				if (element_name && schema && schema.styles) {
434 					// Serialize global styles and element specific styles
435 					serializeStyles('*');
436 					serializeStyles(element_name);
437 				} else {
438 					// Output the styles in the order they are inside the object
439 					for (name in styles) {
440 						value = styles[name];
441 
442 						if (value !== undef && value.length > 0) {
443 							css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
444 						}
445 					}
446 				}
447 
448 				return css;
449 			}
450 		};
451 	};
452 });
453 
454 // Included from: js/tinymce/classes/dom/EventUtils.js
455 
456 /**
457  * EventUtils.js
458  *
459  * Copyright, Moxiecode Systems AB
460  * Released under LGPL License.
461  *
462  * License: http://www.tinymce.com/license
463  * Contributing: http://www.tinymce.com/contributing
464  */
465 
466 /*jshint loopfunc:true*/
467 /*eslint no-loop-func:0 */
468 
469 define("tinymce/dom/EventUtils", [], function() {
470 	"use strict";
471 
472 	var eventExpandoPrefix = "mce-data-";
473 	var mouseEventRe = /^(?:mouse|contextmenu)|click/;
474 	var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1};
475 
476 	/**
477 	 * Binds a native event to a callback on the speified target.
478 	 */
479 	function addEvent(target, name, callback, capture) {
480 		if (target.addEventListener) {
481 			target.addEventListener(name, callback, capture || false);
482 		} else if (target.attachEvent) {
483 			target.attachEvent('on' + name, callback);
484 		}
485 	}
486 
487 	/**
488 	 * Unbinds a native event callback on the specified target.
489 	 */
490 	function removeEvent(target, name, callback, capture) {
491 		if (target.removeEventListener) {
492 			target.removeEventListener(name, callback, capture || false);
493 		} else if (target.detachEvent) {
494 			target.detachEvent('on' + name, callback);
495 		}
496 	}
497 
498 	/**
499 	 * Normalizes a native event object or just adds the event specific methods on a custom event.
500 	 */
501 	function fix(originalEvent, data) {
502 		var name, event = data || {}, undef;
503 
504 		// Dummy function that gets replaced on the delegation state functions
505 		function returnFalse() {
506 			return false;
507 		}
508 
509 		// Dummy function that gets replaced on the delegation state functions
510 		function returnTrue() {
511 			return true;
512 		}
513 
514 		// Copy all properties from the original event
515 		for (name in originalEvent) {
516 			// layerX/layerY is deprecated in Chrome and produces a warning
517 			if (!deprecated[name]) {
518 				event[name] = originalEvent[name];
519 			}
520 		}
521 
522 		// Normalize target IE uses srcElement
523 		if (!event.target) {
524 			event.target = event.srcElement || document;
525 		}
526 
527 		// Calculate pageX/Y if missing and clientX/Y available
528 		if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) {
529 			var eventDoc = event.target.ownerDocument || document;
530 			var doc = eventDoc.documentElement;
531 			var body = eventDoc.body;
532 
533 			event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
534 				( doc && doc.clientLeft || body && body.clientLeft || 0);
535 
536 			event.pageY = originalEvent.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -
537 				( doc && doc.clientTop  || body && body.clientTop  || 0);
538 		}
539 
540 		// Add preventDefault method
541 		event.preventDefault = function() {
542 			event.isDefaultPrevented = returnTrue;
543 
544 			// Execute preventDefault on the original event object
545 			if (originalEvent) {
546 				if (originalEvent.preventDefault) {
547 					originalEvent.preventDefault();
548 				} else {
549 					originalEvent.returnValue = false; // IE
550 				}
551 			}
552 		};
553 
554 		// Add stopPropagation
555 		event.stopPropagation = function() {
556 			event.isPropagationStopped = returnTrue;
557 
558 			// Execute stopPropagation on the original event object
559 			if (originalEvent) {
560 				if (originalEvent.stopPropagation) {
561 					originalEvent.stopPropagation();
562 				} else {
563 					originalEvent.cancelBubble = true; // IE
564 				}
565 			}
566 		};
567 
568 		// Add stopImmediatePropagation
569 		event.stopImmediatePropagation = function() {
570 			event.isImmediatePropagationStopped = returnTrue;
571 			event.stopPropagation();
572 		};
573 
574 		// Add event delegation states
575 		if (!event.isDefaultPrevented) {
576 			event.isDefaultPrevented = returnFalse;
577 			event.isPropagationStopped = returnFalse;
578 			event.isImmediatePropagationStopped = returnFalse;
579 		}
580 
581 		return event;
582 	}
583 
584 	/**
585 	 * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized.
586 	 * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times.
587 	 */
588 	function bindOnReady(win, callback, eventUtils) {
589 		var doc = win.document, event = {type: 'ready'};
590 
591 		if (eventUtils.domLoaded) {
592 			callback(event);
593 			return;
594 		}
595 
596 		// Gets called when the DOM is ready
597 		function readyHandler() {
598 			if (!eventUtils.domLoaded) {
599 				eventUtils.domLoaded = true;
600 				callback(event);
601 			}
602 		}
603 
604 		function waitForDomLoaded() {
605 			// Check complete or interactive state if there is a body
606 			// element on some iframes IE 8 will produce a null body
607 			if (doc.readyState === "complete" || (doc.readyState === "interactive" && doc.body)) {
608 				removeEvent(doc, "readystatechange", waitForDomLoaded);
609 				readyHandler();
610 			}
611 		}
612 
613 		function tryScroll() {
614 			try {
615 				// If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
616 				// http://javascript.nwbox.com/IEContentLoaded/
617 				doc.documentElement.doScroll("left");
618 			} catch (ex) {
619 				setTimeout(tryScroll, 0);
620 				return;
621 			}
622 
623 			readyHandler();
624 		}
625 
626 		// Use W3C method
627 		if (doc.addEventListener) {
628 			if (doc.readyState === "complete") {
629 				readyHandler();
630 			} else {
631 				addEvent(win, 'DOMContentLoaded', readyHandler);
632 			}
633 		} else {
634 			// Use IE method
635 			addEvent(doc, "readystatechange", waitForDomLoaded);
636 
637 			// Wait until we can scroll, when we can the DOM is initialized
638 			if (doc.documentElement.doScroll && win.self === win.top) {
639 				tryScroll();
640 			}
641 		}
642 
643 		// Fallback if any of the above methods should fail for some odd reason
644 		addEvent(win, 'load', readyHandler);
645 	}
646 
647 	/**
648 	 * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers.
649 	 */
650 	function EventUtils() {
651 		var self = this, events = {}, count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
652 
653 		expando = eventExpandoPrefix + (+new Date()).toString(32);
654 		hasMouseEnterLeave = "onmouseenter" in document.documentElement;
655 		hasFocusIn = "onfocusin" in document.documentElement;
656 		mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
657 		count = 1;
658 
659 		// State if the DOMContentLoaded was executed or not
660 		self.domLoaded = false;
661 		self.events = events;
662 
663 		/**
664 		 * Executes all event handler callbacks for a specific event.
665 		 *
666 		 * @private
667 		 * @param {Event} evt Event object.
668 		 * @param {String} id Expando id value to look for.
669 		 */
670 		function executeHandlers(evt, id) {
671 			var callbackList, i, l, callback, container = events[id];
672 
673 			callbackList = container && container[evt.type];
674 			if (callbackList) {
675 				for (i = 0, l = callbackList.length; i < l; i++) {
676 					callback = callbackList[i];
677 
678 					// Check if callback exists might be removed if a unbind is called inside the callback
679 					if (callback && callback.func.call(callback.scope, evt) === false) {
680 						evt.preventDefault();
681 					}
682 
683 					// Should we stop propagation to immediate listeners
684 					if (evt.isImmediatePropagationStopped()) {
685 						return;
686 					}
687 				}
688 			}
689 		}
690 
691 		/**
692 		 * Binds a callback to an event on the specified target.
693 		 *
694 		 * @method bind
695 		 * @param {Object} target Target node/window or custom object.
696 		 * @param {String} names Name of the event to bind.
697 		 * @param {function} callback Callback function to execute when the event occurs.
698 		 * @param {Object} scope Scope to call the callback function on, defaults to target.
699 		 * @return {function} Callback function that got bound.
700 		 */
701 		self.bind = function(target, names, callback, scope) {
702 			var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
703 
704 			// Native event handler function patches the event and executes the callbacks for the expando
705 			function defaultNativeHandler(evt) {
706 				executeHandlers(fix(evt || win.event), id);
707 			}
708 
709 			// Don't bind to text nodes or comments
710 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
711 				return;
712 			}
713 
714 			// Create or get events id for the target
715 			if (!target[expando]) {
716 				id = count++;
717 				target[expando] = id;
718 				events[id] = {};
719 			} else {
720 				id = target[expando];
721 			}
722 
723 			// Setup the specified scope or use the target as a default
724 			scope = scope || target;
725 
726 			// Split names and bind each event, enables you to bind multiple events with one call
727 			names = names.split(' ');
728 			i = names.length;
729 			while (i--) {
730 				name = names[i];
731 				nativeHandler = defaultNativeHandler;
732 				fakeName = capture = false;
733 
734 				// Use ready instead of DOMContentLoaded
735 				if (name === "DOMContentLoaded") {
736 					name = "ready";
737 				}
738 
739 				// DOM is already ready
740 				if (self.domLoaded && name === "ready" && target.readyState == 'complete') {
741 					callback.call(scope, fix({type: name}));
742 					continue;
743 				}
744 
745 				// Handle mouseenter/mouseleaver
746 				if (!hasMouseEnterLeave) {
747 					fakeName = mouseEnterLeave[name];
748 
749 					if (fakeName) {
750 						nativeHandler = function(evt) {
751 							var current, related;
752 
753 							current = evt.currentTarget;
754 							related = evt.relatedTarget;
755 
756 							// Check if related is inside the current target if it's not then the event should
757 							// be ignored since it's a mouseover/mouseout inside the element
758 							if (related && current.contains) {
759 								// Use contains for performance
760 								related = current.contains(related);
761 							} else {
762 								while (related && related !== current) {
763 									related = related.parentNode;
764 								}
765 							}
766 
767 							// Fire fake event
768 							if (!related) {
769 								evt = fix(evt || win.event);
770 								evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
771 								evt.target = current;
772 								executeHandlers(evt, id);
773 							}
774 						};
775 					}
776 				}
777 
778 				// Fake bubbeling of focusin/focusout
779 				if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
780 					capture = true;
781 					fakeName = name === "focusin" ? "focus" : "blur";
782 					nativeHandler = function(evt) {
783 						evt = fix(evt || win.event);
784 						evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
785 						executeHandlers(evt, id);
786 					};
787 				}
788 
789 				// Setup callback list and bind native event
790 				callbackList = events[id][name];
791 				if (!callbackList) {
792 					events[id][name] = callbackList = [{func: callback, scope: scope}];
793 					callbackList.fakeName = fakeName;
794 					callbackList.capture = capture;
795 
796 					// Add the nativeHandler to the callback list so that we can later unbind it
797 					callbackList.nativeHandler = nativeHandler;
798 
799 					// Check if the target has native events support
800 
801 					if (name === "ready") {
802 						bindOnReady(target, nativeHandler, self);
803 					} else {
804 						addEvent(target, fakeName || name, nativeHandler, capture);
805 					}
806 				} else {
807 					if (name === "ready" && self.domLoaded) {
808 						callback({type: name});
809 					} else {
810 						// If it already has an native handler then just push the callback
811 						callbackList.push({func: callback, scope: scope});
812 					}
813 				}
814 			}
815 
816 			target = callbackList = 0; // Clean memory for IE
817 
818 			return callback;
819 		};
820 
821 		/**
822 		 * Unbinds the specified event by name, name and callback or all events on the target.
823 		 *
824 		 * @method unbind
825 		 * @param {Object} target Target node/window or custom object.
826 		 * @param {String} names Optional event name to unbind.
827 		 * @param {function} callback Optional callback function to unbind.
828 		 * @return {EventUtils} Event utils instance.
829 		 */
830 		self.unbind = function(target, names, callback) {
831 			var id, callbackList, i, ci, name, eventMap;
832 
833 			// Don't bind to text nodes or comments
834 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
835 				return self;
836 			}
837 
838 			// Unbind event or events if the target has the expando
839 			id = target[expando];
840 			if (id) {
841 				eventMap = events[id];
842 
843 				// Specific callback
844 				if (names) {
845 					names = names.split(' ');
846 					i = names.length;
847 					while (i--) {
848 						name = names[i];
849 						callbackList = eventMap[name];
850 
851 						// Unbind the event if it exists in the map
852 						if (callbackList) {
853 							// Remove specified callback
854 							if (callback) {
855 								ci = callbackList.length;
856 								while (ci--) {
857 									if (callbackList[ci].func === callback) {
858 										var nativeHandler = callbackList.nativeHandler;
859 										var fakeName = callbackList.fakeName, capture = callbackList.capture;
860 
861 										// Clone callbackList since unbind inside a callback would otherwise break the handlers loop
862 										callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
863 										callbackList.nativeHandler = nativeHandler;
864 										callbackList.fakeName = fakeName;
865 										callbackList.capture = capture;
866 
867 										eventMap[name] = callbackList;
868 									}
869 								}
870 							}
871 
872 							// Remove all callbacks if there isn't a specified callback or there is no callbacks left
873 							if (!callback || callbackList.length === 0) {
874 								delete eventMap[name];
875 								removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
876 							}
877 						}
878 					}
879 				} else {
880 					// All events for a specific element
881 					for (name in eventMap) {
882 						callbackList = eventMap[name];
883 						removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
884 					}
885 
886 					eventMap = {};
887 				}
888 
889 				// Check if object is empty, if it isn't then we won't remove the expando map
890 				for (name in eventMap) {
891 					return self;
892 				}
893 
894 				// Delete event object
895 				delete events[id];
896 
897 				// Remove expando from target
898 				try {
899 					// IE will fail here since it can't delete properties from window
900 					delete target[expando];
901 				} catch (ex) {
902 					// IE will set it to null
903 					target[expando] = null;
904 				}
905 			}
906 
907 			return self;
908 		};
909 
910 		/**
911 		 * Fires the specified event on the specified target.
912 		 *
913 		 * @method fire
914 		 * @param {Object} target Target node/window or custom object.
915 		 * @param {String} name Event name to fire.
916 		 * @param {Object} args Optional arguments to send to the observers.
917 		 * @return {EventUtils} Event utils instance.
918 		 */
919 		self.fire = function(target, name, args) {
920 			var id;
921 
922 			// Don't bind to text nodes or comments
923 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
924 				return self;
925 			}
926 
927 			// Build event object by patching the args
928 			args = fix(null, args);
929 			args.type = name;
930 			args.target = target;
931 
932 			do {
933 				// Found an expando that means there is listeners to execute
934 				id = target[expando];
935 				if (id) {
936 					executeHandlers(args, id);
937 				}
938 
939 				// Walk up the DOM
940 				target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
941 			} while (target && !args.isPropagationStopped());
942 
943 			return self;
944 		};
945 
946 		/**
947 		 * Removes all bound event listeners for the specified target. This will also remove any bound
948 		 * listeners to child nodes within that target.
949 		 *
950 		 * @method clean
951 		 * @param {Object} target Target node/window object.
952 		 * @return {EventUtils} Event utils instance.
953 		 */
954 		self.clean = function(target) {
955 			var i, children, unbind = self.unbind;
956 
957 			// Don't bind to text nodes or comments
958 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
959 				return self;
960 			}
961 
962 			// Unbind any element on the specificed target
963 			if (target[expando]) {
964 				unbind(target);
965 			}
966 
967 			// Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
968 			if (!target.getElementsByTagName) {
969 				target = target.document;
970 			}
971 
972 			// Remove events from each child element
973 			if (target && target.getElementsByTagName) {
974 				unbind(target);
975 
976 				children = target.getElementsByTagName('*');
977 				i = children.length;
978 				while (i--) {
979 					target = children[i];
980 
981 					if (target[expando]) {
982 						unbind(target);
983 					}
984 				}
985 			}
986 
987 			return self;
988 		};
989 
990 		/**
991 		 * Destroys the event object. Call this on IE to remove memory leaks.
992 		 */
993 		self.destroy = function() {
994 			events = {};
995 		};
996 
997 		// Legacy function for canceling events
998 		self.cancel = function(e) {
999 			if (e) {
1000 				e.preventDefault();
1001 				e.stopImmediatePropagation();
1002 			}
1003 
1004 			return false;
1005 		};
1006 	}
1007 
1008 	EventUtils.Event = new EventUtils();
1009 	EventUtils.Event.bind(window, 'ready', function() {});
1010 
1011 	return EventUtils;
1012 });
1013 
1014 // Included from: js/tinymce/classes/dom/TreeWalker.js
1015 
1016 /**
1017  * TreeWalker.js
1018  *
1019  * Copyright, Moxiecode Systems AB
1020  * Released under LGPL License.
1021  *
1022  * License: http://www.tinymce.com/license
1023  * Contributing: http://www.tinymce.com/contributing
1024  */
1025 
1026 /**
1027  * TreeWalker class enables you to walk the DOM in a linear manner.
1028  *
1029  * @class tinymce.dom.TreeWalker
1030  */
1031 define("tinymce/dom/TreeWalker", [], function() {
1032 	return function(start_node, root_node) {
1033 		var node = start_node;
1034 
1035 		function findSibling(node, start_name, sibling_name, shallow) {
1036 			var sibling, parent;
1037 
1038 			if (node) {
1039 				// Walk into nodes if it has a start
1040 				if (!shallow && node[start_name]) {
1041 					return node[start_name];
1042 				}
1043 
1044 				// Return the sibling if it has one
1045 				if (node != root_node) {
1046 					sibling = node[sibling_name];
1047 					if (sibling) {
1048 						return sibling;
1049 					}
1050 
1051 					// Walk up the parents to look for siblings
1052 					for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
1053 						sibling = parent[sibling_name];
1054 						if (sibling) {
1055 							return sibling;
1056 						}
1057 					}
1058 				}
1059 			}
1060 		}
1061 
1062 		/**
1063 		 * Returns the current node.
1064 		 *
1065 		 * @method current
1066 		 * @return {Node} Current node where the walker is.
1067 		 */
1068 		this.current = function() {
1069 			return node;
1070 		};
1071 
1072 		/**
1073 		 * Walks to the next node in tree.
1074 		 *
1075 		 * @method next
1076 		 * @return {Node} Current node where the walker is after moving to the next node.
1077 		 */
1078 		this.next = function(shallow) {
1079 			node = findSibling(node, 'firstChild', 'nextSibling', shallow);
1080 			return node;
1081 		};
1082 
1083 		/**
1084 		 * Walks to the previous node in tree.
1085 		 *
1086 		 * @method prev
1087 		 * @return {Node} Current node where the walker is after moving to the previous node.
1088 		 */
1089 		this.prev = function(shallow) {
1090 			node = findSibling(node, 'lastChild', 'previousSibling', shallow);
1091 			return node;
1092 		};
1093 	};
1094 });
1095 
1096 // Included from: js/tinymce/classes/util/Tools.js
1097 
1098 /**
1099  * Tools.js
1100  *
1101  * Copyright, Moxiecode Systems AB
1102  * Released under LGPL License.
1103  *
1104  * License: http://www.tinymce.com/license
1105  * Contributing: http://www.tinymce.com/contributing
1106  */
1107 
1108 /**
1109  * This class contains various utlity functions. These are also exposed
1110  * directly on the tinymce namespace.
1111  *
1112  * @class tinymce.util.Tools
1113  */
1114 define("tinymce/util/Tools", [], function() {
1115 	/**
1116 	 * Removes whitespace from the beginning and end of a string.
1117 	 *
1118 	 * @method trim
1119 	 * @param {String} s String to remove whitespace from.
1120 	 * @return {String} New string with removed whitespace.
1121 	 */
1122 	var whiteSpaceRegExp = /^\s*|\s*$/g;
1123 
1124 	function trim(str) {
1125 		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
1126 	}
1127 
1128 	/**
1129 	 * Returns true/false if the object is an array or not.
1130 	 *
1131 	 * @method isArray
1132 	 * @param {Object} obj Object to check.
1133 	 * @return {boolean} true/false state if the object is an array or not.
1134 	 */
1135 	var isArray = Array.isArray || function(obj) {
1136 		return Object.prototype.toString.call(obj) === "[object Array]";
1137 	};
1138 
1139 	/**
1140 	 * Checks if a object is of a specific type for example an array.
1141 	 *
1142 	 * @method is
1143 	 * @param {Object} o Object to check type of.
1144 	 * @param {string} t Optional type to check for.
1145 	 * @return {Boolean} true/false if the object is of the specified type.
1146 	 */
1147 	function is(o, t) {
1148 		if (!t) {
1149 			return o !== undefined;
1150 		}
1151 
1152 		if (t == 'array' && isArray(o)) {
1153 			return true;
1154 		}
1155 
1156 		return typeof(o) == t;
1157 	}
1158 
1159 	/**
1160 	 * Converts the specified object into a real JavaScript array.
1161 	 *
1162 	 * @method toArray
1163 	 * @param {Object} obj Object to convert into array.
1164 	 * @return {Array} Array object based in input.
1165 	 */
1166 	function toArray(obj) {
1167 		var array = [], i, l;
1168 
1169 		for (i = 0, l = obj.length; i < l; i++) {
1170 			array[i] = obj[i];
1171 		}
1172 
1173 		return array;
1174 	}
1175 
1176 	/**
1177 	 * Makes a name/object map out of an array with names.
1178 	 *
1179 	 * @method makeMap
1180 	 * @param {Array/String} items Items to make map out of.
1181 	 * @param {String} delim Optional delimiter to split string by.
1182 	 * @param {Object} map Optional map to add items to.
1183 	 * @return {Object} Name/value map of items.
1184 	 */
1185 	function makeMap(items, delim, map) {
1186 		var i;
1187 
1188 		items = items || [];
1189 		delim = delim || ',';
1190 
1191 		if (typeof(items) == "string") {
1192 			items = items.split(delim);
1193 		}
1194 
1195 		map = map || {};
1196 
1197 		i = items.length;
1198 		while (i--) {
1199 			map[items[i]] = {};
1200 		}
1201 
1202 		return map;
1203 	}
1204 
1205 	/**
1206 	 * Performs an iteration of all items in a collection such as an object or array. This method will execure the
1207 	 * callback function for each item in the collection, if the callback returns false the iteration will terminate.
1208 	 * The callback has the following format: cb(value, key_or_index).
1209 	 *
1210 	 * @method each
1211 	 * @param {Object} o Collection to iterate.
1212 	 * @param {function} cb Callback function to execute for each item.
1213 	 * @param {Object} s Optional scope to execute the callback in.
1214 	 * @example
1215 	 * // Iterate an array
1216 	 * tinymce.each([1,2,3], function(v, i) {
1217 	 *     console.debug("Value: " + v + ", Index: " + i);
1218 	 * });
1219 	 *
1220 	 * // Iterate an object
1221 	 * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
1222 	 *     console.debug("Value: " + v + ", Key: " + k);
1223 	 * });
1224 	 */
1225 	function each(o, cb, s) {
1226 		var n, l;
1227 
1228 		if (!o) {
1229 			return 0;
1230 		}
1231 
1232 		s = s || o;
1233 
1234 		if (o.length !== undefined) {
1235 			// Indexed arrays, needed for Safari
1236 			for (n = 0, l = o.length; n < l; n++) {
1237 				if (cb.call(s, o[n], n, o) === false) {
1238 					return 0;
1239 				}
1240 			}
1241 		} else {
1242 			// Hashtables
1243 			for (n in o) {
1244 				if (o.hasOwnProperty(n)) {
1245 					if (cb.call(s, o[n], n, o) === false) {
1246 						return 0;
1247 					}
1248 				}
1249 			}
1250 		}
1251 
1252 		return 1;
1253 	}
1254 
1255 	/**
1256 	 * Creates a new array by the return value of each iteration function call. This enables you to convert
1257 	 * one array list into another.
1258 	 *
1259 	 * @method map
1260 	 * @param {Array} a Array of items to iterate.
1261 	 * @param {function} f Function to call for each item. It's return value will be the new value.
1262 	 * @return {Array} Array with new values based on function return values.
1263 	 */
1264 	function map(a, f) {
1265 		var o = [];
1266 
1267 		each(a, function(v) {
1268 			o.push(f(v));
1269 		});
1270 
1271 		return o;
1272 	}
1273 
1274 	/**
1275 	 * Filters out items from the input array by calling the specified function for each item.
1276 	 * If the function returns false the item will be excluded if it returns true it will be included.
1277 	 *
1278 	 * @method grep
1279 	 * @param {Array} a Array of items to loop though.
1280 	 * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
1281 	 * @return {Array} New array with values imported and filtered based in input.
1282 	 * @example
1283 	 * // Filter out some items, this will return an array with 4 and 5
1284 	 * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
1285 	 */
1286 	function grep(a, f) {
1287 		var o = [];
1288 
1289 		each(a, function(v) {
1290 			if (!f || f(v)) {
1291 				o.push(v);
1292 			}
1293 		});
1294 
1295 		return o;
1296 	}
1297 
1298 	/**
1299 	 * Creates a class, subclass or static singleton.
1300 	 * More details on this method can be found in the Wiki.
1301 	 *
1302 	 * @method create
1303 	 * @param {String} s Class name, inheritage and prefix.
1304 	 * @param {Object} p Collection of methods to add to the class.
1305 	 * @param {Object} root Optional root object defaults to the global window object.
1306 	 * @example
1307 	 * // Creates a basic class
1308 	 * tinymce.create('tinymce.somepackage.SomeClass', {
1309 	 *     SomeClass: function() {
1310 	 *         // Class constructor
1311 	 *     },
1312 	 *
1313 	 *     method: function() {
1314 	 *         // Some method
1315 	 *     }
1316 	 * });
1317 	 *
1318 	 * // Creates a basic subclass class
1319 	 * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
1320 	 *     SomeSubClass: function() {
1321 	 *         // Class constructor
1322 	 *         this.parent(); // Call parent constructor
1323 	 *     },
1324 	 *
1325 	 *     method: function() {
1326 	 *         // Some method
1327 	 *         this.parent(); // Call parent method
1328 	 *     },
1329 	 *
1330 	 *     'static': {
1331 	 *         staticMethod: function() {
1332 	 *             // Static method
1333 	 *         }
1334 	 *     }
1335 	 * });
1336 	 *
1337 	 * // Creates a singleton/static class
1338 	 * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
1339 	 *     method: function() {
1340 	 *         // Some method
1341 	 *     }
1342 	 * });
1343 	 */
1344 	function create(s, p, root) {
1345 		var self = this, sp, ns, cn, scn, c, de = 0;
1346 
1347 		// Parse : <prefix> <class>:<super class>
1348 		s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
1349 		cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
1350 
1351 		// Create namespace for new class
1352 		ns = self.createNS(s[3].replace(/\.\w+$/, ''), root);
1353 
1354 		// Class already exists
1355 		if (ns[cn]) {
1356 			return;
1357 		}
1358 
1359 		// Make pure static class
1360 		if (s[2] == 'static') {
1361 			ns[cn] = p;
1362 
1363 			if (this.onCreate) {
1364 				this.onCreate(s[2], s[3], ns[cn]);
1365 			}
1366 
1367 			return;
1368 		}
1369 
1370 		// Create default constructor
1371 		if (!p[cn]) {
1372 			p[cn] = function() {};
1373 			de = 1;
1374 		}
1375 
1376 		// Add constructor and methods
1377 		ns[cn] = p[cn];
1378 		self.extend(ns[cn].prototype, p);
1379 
1380 		// Extend
1381 		if (s[5]) {
1382 			sp = self.resolve(s[5]).prototype;
1383 			scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
1384 
1385 			// Extend constructor
1386 			c = ns[cn];
1387 			if (de) {
1388 				// Add passthrough constructor
1389 				ns[cn] = function() {
1390 					return sp[scn].apply(this, arguments);
1391 				};
1392 			} else {
1393 				// Add inherit constructor
1394 				ns[cn] = function() {
1395 					this.parent = sp[scn];
1396 					return c.apply(this, arguments);
1397 				};
1398 			}
1399 			ns[cn].prototype[cn] = ns[cn];
1400 
1401 			// Add super methods
1402 			self.each(sp, function(f, n) {
1403 				ns[cn].prototype[n] = sp[n];
1404 			});
1405 
1406 			// Add overridden methods
1407 			self.each(p, function(f, n) {
1408 				// Extend methods if needed
1409 				if (sp[n]) {
1410 					ns[cn].prototype[n] = function() {
1411 						this.parent = sp[n];
1412 						return f.apply(this, arguments);
1413 					};
1414 				} else {
1415 					if (n != cn) {
1416 						ns[cn].prototype[n] = f;
1417 					}
1418 				}
1419 			});
1420 		}
1421 
1422 		// Add static methods
1423 		/*jshint sub:true*/
1424 		self.each(p['static'], function(f, n) {
1425 			ns[cn][n] = f;
1426 		});
1427 	}
1428 
1429 	/**
1430 	 * Returns the index of a value in an array, this method will return -1 if the item wasn't found.
1431 	 *
1432 	 * @method inArray
1433 	 * @param {Array} a Array/Object to search for value in.
1434 	 * @param {Object} v Value to check for inside the array.
1435 	 * @return {Number/String} Index of item inside the array inside an object. Or -1 if it wasn't found.
1436 	 * @example
1437 	 * // Get index of value in array this will alert 1 since 2 is at that index
1438 	 * alert(tinymce.inArray([1,2,3], 2));
1439 	 */
1440 	function inArray(a, v) {
1441 		var i, l;
1442 
1443 		if (a) {
1444 			for (i = 0, l = a.length; i < l; i++) {
1445 				if (a[i] === v) {
1446 					return i;
1447 				}
1448 			}
1449 		}
1450 
1451 		return -1;
1452 	}
1453 
1454 	function extend(obj, ext) {
1455 		var i, l, name, args = arguments, value;
1456 
1457 		for (i = 1, l = args.length; i < l; i++) {
1458 			ext = args[i];
1459 			for (name in ext) {
1460 				if (ext.hasOwnProperty(name)) {
1461 					value = ext[name];
1462 
1463 					if (value !== undefined) {
1464 						obj[name] = value;
1465 					}
1466 				}
1467 			}
1468 		}
1469 
1470 		return obj;
1471 	}
1472 
1473 	/**
1474 	 * Executed the specified function for each item in a object tree.
1475 	 *
1476 	 * @method walk
1477 	 * @param {Object} o Object tree to walk though.
1478 	 * @param {function} f Function to call for each item.
1479 	 * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
1480 	 * @param {String} s Optional scope to execute the function in.
1481 	 */
1482 	function walk(o, f, n, s) {
1483 		s = s || this;
1484 
1485 		if (o) {
1486 			if (n) {
1487 				o = o[n];
1488 			}
1489 
1490 			each(o, function(o, i) {
1491 				if (f.call(s, o, i, n) === false) {
1492 					return false;
1493 				}
1494 
1495 				walk(o, f, n, s);
1496 			});
1497 		}
1498 	}
1499 
1500 	/**
1501 	 * Creates a namespace on a specific object.
1502 	 *
1503 	 * @method createNS
1504 	 * @param {String} n Namespace to create for example a.b.c.d.
1505 	 * @param {Object} o Optional object to add namespace to, defaults to window.
1506 	 * @return {Object} New namespace object the last item in path.
1507 	 * @example
1508 	 * // Create some namespace
1509 	 * tinymce.createNS('tinymce.somepackage.subpackage');
1510 	 *
1511 	 * // Add a singleton
1512 	 * var tinymce.somepackage.subpackage.SomeSingleton = {
1513 	 *     method: function() {
1514 	 *         // Some method
1515 	 *     }
1516 	 * };
1517 	 */
1518 	function createNS(n, o) {
1519 		var i, v;
1520 
1521 		o = o || window;
1522 
1523 		n = n.split('.');
1524 		for (i = 0; i < n.length; i++) {
1525 			v = n[i];
1526 
1527 			if (!o[v]) {
1528 				o[v] = {};
1529 			}
1530 
1531 			o = o[v];
1532 		}
1533 
1534 		return o;
1535 	}
1536 
1537 	/**
1538 	 * Resolves a string and returns the object from a specific structure.
1539 	 *
1540 	 * @method resolve
1541 	 * @param {String} n Path to resolve for example a.b.c.d.
1542 	 * @param {Object} o Optional object to search though, defaults to window.
1543 	 * @return {Object} Last object in path or null if it couldn't be resolved.
1544 	 * @example
1545 	 * // Resolve a path into an object reference
1546 	 * var obj = tinymce.resolve('a.b.c.d');
1547 	 */
1548 	function resolve(n, o) {
1549 		var i, l;
1550 
1551 		o = o || window;
1552 
1553 		n = n.split('.');
1554 		for (i = 0, l = n.length; i < l; i++) {
1555 			o = o[n[i]];
1556 
1557 			if (!o) {
1558 				break;
1559 			}
1560 		}
1561 
1562 		return o;
1563 	}
1564 
1565 	/**
1566 	 * Splits a string but removes the whitespace before and after each value.
1567 	 *
1568 	 * @method explode
1569 	 * @param {string} s String to split.
1570 	 * @param {string} d Delimiter to split by.
1571 	 * @example
1572 	 * // Split a string into an array with a,b,c
1573 	 * var arr = tinymce.explode('a, b,   c');
1574 	 */
1575 	function explode(s, d) {
1576 		if (!s || is(s, 'array')) {
1577 			return s;
1578 		}
1579 
1580 		return map(s.split(d || ','), trim);
1581 	}
1582 
1583 	return {
1584 		trim: trim,
1585 		isArray: isArray,
1586 		is: is,
1587 		toArray: toArray,
1588 		makeMap: makeMap,
1589 		each: each,
1590 		map: map,
1591 		grep: grep,
1592 		inArray: inArray,
1593 		extend: extend,
1594 		create: create,
1595 		walk: walk,
1596 		createNS: createNS,
1597 		resolve: resolve,
1598 		explode: explode
1599 	};
1600 });
1601 
1602 // Included from: js/tinymce/classes/dom/Range.js
1603 
1604 /**
1605  * Range.js
1606  *
1607  * Copyright, Moxiecode Systems AB
1608  * Released under LGPL License.
1609  *
1610  * License: http://www.tinymce.com/license
1611  * Contributing: http://www.tinymce.com/contributing
1612  */
1613 
1614 define("tinymce/dom/Range", [
1615 	"tinymce/util/Tools"
1616 ], function(Tools) {
1617 	// Range constructor
1618 	function Range(dom) {
1619 		var self = this,
1620 			doc = dom.doc,
1621 			EXTRACT = 0,
1622 			CLONE = 1,
1623 			DELETE = 2,
1624 			TRUE = true,
1625 			FALSE = false,
1626 			START_OFFSET = 'startOffset',
1627 			START_CONTAINER = 'startContainer',
1628 			END_CONTAINER = 'endContainer',
1629 			END_OFFSET = 'endOffset',
1630 			extend = Tools.extend,
1631 			nodeIndex = dom.nodeIndex;
1632 
1633 		function createDocumentFragment() {
1634 			return doc.createDocumentFragment();
1635 		}
1636 
1637 		function setStart(n, o) {
1638 			_setEndPoint(TRUE, n, o);
1639 		}
1640 
1641 		function setEnd(n, o) {
1642 			_setEndPoint(FALSE, n, o);
1643 		}
1644 
1645 		function setStartBefore(n) {
1646 			setStart(n.parentNode, nodeIndex(n));
1647 		}
1648 
1649 		function setStartAfter(n) {
1650 			setStart(n.parentNode, nodeIndex(n) + 1);
1651 		}
1652 
1653 		function setEndBefore(n) {
1654 			setEnd(n.parentNode, nodeIndex(n));
1655 		}
1656 
1657 		function setEndAfter(n) {
1658 			setEnd(n.parentNode, nodeIndex(n) + 1);
1659 		}
1660 
1661 		function collapse(ts) {
1662 			if (ts) {
1663 				self[END_CONTAINER] = self[START_CONTAINER];
1664 				self[END_OFFSET] = self[START_OFFSET];
1665 			} else {
1666 				self[START_CONTAINER] = self[END_CONTAINER];
1667 				self[START_OFFSET] = self[END_OFFSET];
1668 			}
1669 
1670 			self.collapsed = TRUE;
1671 		}
1672 
1673 		function selectNode(n) {
1674 			setStartBefore(n);
1675 			setEndAfter(n);
1676 		}
1677 
1678 		function selectNodeContents(n) {
1679 			setStart(n, 0);
1680 			setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
1681 		}
1682 
1683 		function compareBoundaryPoints(h, r) {
1684 			var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET],
1685 			rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
1686 
1687 			// Check START_TO_START
1688 			if (h === 0) {
1689 				return _compareBoundaryPoints(sc, so, rsc, rso);
1690 			}
1691 
1692 			// Check START_TO_END
1693 			if (h === 1) {
1694 				return _compareBoundaryPoints(ec, eo, rsc, rso);
1695 			}
1696 
1697 			// Check END_TO_END
1698 			if (h === 2) {
1699 				return _compareBoundaryPoints(ec, eo, rec, reo);
1700 			}
1701 
1702 			// Check END_TO_START
1703 			if (h === 3) {
1704 				return _compareBoundaryPoints(sc, so, rec, reo);
1705 			}
1706 		}
1707 
1708 		function deleteContents() {
1709 			_traverse(DELETE);
1710 		}
1711 
1712 		function extractContents() {
1713 			return _traverse(EXTRACT);
1714 		}
1715 
1716 		function cloneContents() {
1717 			return _traverse(CLONE);
1718 		}
1719 
1720 		function insertNode(n) {
1721 			var startContainer = this[START_CONTAINER],
1722 				startOffset = this[START_OFFSET], nn, o;
1723 
1724 			// Node is TEXT_NODE or CDATA
1725 			if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
1726 				if (!startOffset) {
1727 					// At the start of text
1728 					startContainer.parentNode.insertBefore(n, startContainer);
1729 				} else if (startOffset >= startContainer.nodeValue.length) {
1730 					// At the end of text
1731 					dom.insertAfter(n, startContainer);
1732 				} else {
1733 					// Middle, need to split
1734 					nn = startContainer.splitText(startOffset);
1735 					startContainer.parentNode.insertBefore(n, nn);
1736 				}
1737 			} else {
1738 				// Insert element node
1739 				if (startContainer.childNodes.length > 0) {
1740 					o = startContainer.childNodes[startOffset];
1741 				}
1742 
1743 				if (o) {
1744 					startContainer.insertBefore(n, o);
1745 				} else {
1746 					if (startContainer.nodeType == 3) {
1747 						dom.insertAfter(n, startContainer);
1748 					} else {
1749 						startContainer.appendChild(n);
1750 					}
1751 				}
1752 			}
1753 		}
1754 
1755 		function surroundContents(n) {
1756 			var f = self.extractContents();
1757 
1758 			self.insertNode(n);
1759 			n.appendChild(f);
1760 			self.selectNode(n);
1761 		}
1762 
1763 		function cloneRange() {
1764 			return extend(new Range(dom), {
1765 				startContainer: self[START_CONTAINER],
1766 				startOffset: self[START_OFFSET],
1767 				endContainer: self[END_CONTAINER],
1768 				endOffset: self[END_OFFSET],
1769 				collapsed: self.collapsed,
1770 				commonAncestorContainer: self.commonAncestorContainer
1771 			});
1772 		}
1773 
1774 		// Private methods
1775 
1776 		function _getSelectedNode(container, offset) {
1777 			var child;
1778 
1779 			if (container.nodeType == 3 /* TEXT_NODE */) {
1780 				return container;
1781 			}
1782 
1783 			if (offset < 0) {
1784 				return container;
1785 			}
1786 
1787 			child = container.firstChild;
1788 			while (child && offset > 0) {
1789 				--offset;
1790 				child = child.nextSibling;
1791 			}
1792 
1793 			if (child) {
1794 				return child;
1795 			}
1796 
1797 			return container;
1798 		}
1799 
1800 		function _isCollapsed() {
1801 			return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]);
1802 		}
1803 
1804 		function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
1805 			var c, offsetC, n, cmnRoot, childA, childB;
1806 
1807 			// In the first case the boundary-points have the same container. A is before B
1808 			// if its offset is less than the offset of B, A is equal to B if its offset is
1809 			// equal to the offset of B, and A is after B if its offset is greater than the
1810 			// offset of B.
1811 			if (containerA == containerB) {
1812 				if (offsetA == offsetB) {
1813 					return 0; // equal
1814 				}
1815 
1816 				if (offsetA < offsetB) {
1817 					return -1; // before
1818 				}
1819 
1820 				return 1; // after
1821 			}
1822 
1823 			// In the second case a child node C of the container of A is an ancestor
1824 			// container of B. In this case, A is before B if the offset of A is less than or
1825 			// equal to the index of the child node C and A is after B otherwise.
1826 			c = containerB;
1827 			while (c && c.parentNode != containerA) {
1828 				c = c.parentNode;
1829 			}
1830 
1831 			if (c) {
1832 				offsetC = 0;
1833 				n = containerA.firstChild;
1834 
1835 				while (n != c && offsetC < offsetA) {
1836 					offsetC++;
1837 					n = n.nextSibling;
1838 				}
1839 
1840 				if (offsetA <= offsetC) {
1841 					return -1; // before
1842 				}
1843 
1844 				return 1; // after
1845 			}
1846 
1847 			// In the third case a child node C of the container of B is an ancestor container
1848 			// of A. In this case, A is before B if the index of the child node C is less than
1849 			// the offset of B and A is after B otherwise.
1850 			c = containerA;
1851 			while (c && c.parentNode != containerB) {
1852 				c = c.parentNode;
1853 			}
1854 
1855 			if (c) {
1856 				offsetC = 0;
1857 				n = containerB.firstChild;
1858 
1859 				while (n != c && offsetC < offsetB) {
1860 					offsetC++;
1861 					n = n.nextSibling;
1862 				}
1863 
1864 				if (offsetC < offsetB) {
1865 					return -1; // before
1866 				}
1867 
1868 				return 1; // after
1869 			}
1870 
1871 			// In the fourth case, none of three other cases hold: the containers of A and B
1872 			// are siblings or descendants of sibling nodes. In this case, A is before B if
1873 			// the container of A is before the container of B in a pre-order traversal of the
1874 			// Ranges' context tree and A is after B otherwise.
1875 			cmnRoot = dom.findCommonAncestor(containerA, containerB);
1876 			childA = containerA;
1877 
1878 			while (childA && childA.parentNode != cmnRoot) {
1879 				childA = childA.parentNode;
1880 			}
1881 
1882 			if (!childA) {
1883 				childA = cmnRoot;
1884 			}
1885 
1886 			childB = containerB;
1887 			while (childB && childB.parentNode != cmnRoot) {
1888 				childB = childB.parentNode;
1889 			}
1890 
1891 			if (!childB) {
1892 				childB = cmnRoot;
1893 			}
1894 
1895 			if (childA == childB) {
1896 				return 0; // equal
1897 			}
1898 
1899 			n = cmnRoot.firstChild;
1900 			while (n) {
1901 				if (n == childA) {
1902 					return -1; // before
1903 				}
1904 
1905 				if (n == childB) {
1906 					return 1; // after
1907 				}
1908 
1909 				n = n.nextSibling;
1910 			}
1911 		}
1912 
1913 		function _setEndPoint(st, n, o) {
1914 			var ec, sc;
1915 
1916 			if (st) {
1917 				self[START_CONTAINER] = n;
1918 				self[START_OFFSET] = o;
1919 			} else {
1920 				self[END_CONTAINER] = n;
1921 				self[END_OFFSET] = o;
1922 			}
1923 
1924 			// If one boundary-point of a Range is set to have a root container
1925 			// other than the current one for the Range, the Range is collapsed to
1926 			// the new position. This enforces the restriction that both boundary-
1927 			// points of a Range must have the same root container.
1928 			ec = self[END_CONTAINER];
1929 			while (ec.parentNode) {
1930 				ec = ec.parentNode;
1931 			}
1932 
1933 			sc = self[START_CONTAINER];
1934 			while (sc.parentNode) {
1935 				sc = sc.parentNode;
1936 			}
1937 
1938 			if (sc == ec) {
1939 				// The start position of a Range is guaranteed to never be after the
1940 				// end position. To enforce this restriction, if the start is set to
1941 				// be at a position after the end, the Range is collapsed to that
1942 				// position.
1943 				if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
1944 					self.collapse(st);
1945 				}
1946 			} else {
1947 				self.collapse(st);
1948 			}
1949 
1950 			self.collapsed = _isCollapsed();
1951 			self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
1952 		}
1953 
1954 		function _traverse(how) {
1955 			var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
1956 
1957 			if (self[START_CONTAINER] == self[END_CONTAINER]) {
1958 				return _traverseSameContainer(how);
1959 			}
1960 
1961 			for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
1962 				if (p == self[START_CONTAINER]) {
1963 					return _traverseCommonStartContainer(c, how);
1964 				}
1965 
1966 				++endContainerDepth;
1967 			}
1968 
1969 			for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
1970 				if (p == self[END_CONTAINER]) {
1971 					return _traverseCommonEndContainer(c, how);
1972 				}
1973 
1974 				++startContainerDepth;
1975 			}
1976 
1977 			depthDiff = startContainerDepth - endContainerDepth;
1978 
1979 			startNode = self[START_CONTAINER];
1980 			while (depthDiff > 0) {
1981 				startNode = startNode.parentNode;
1982 				depthDiff--;
1983 			}
1984 
1985 			endNode = self[END_CONTAINER];
1986 			while (depthDiff < 0) {
1987 				endNode = endNode.parentNode;
1988 				depthDiff++;
1989 			}
1990 
1991 			// ascend the ancestor hierarchy until we have a common parent.
1992 			for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
1993 				startNode = sp;
1994 				endNode = ep;
1995 			}
1996 
1997 			return _traverseCommonAncestors(startNode, endNode, how);
1998 		}
1999 
2000 		function _traverseSameContainer(how) {
2001 			var frag, s, sub, n, cnt, sibling, xferNode, start, len;
2002 
2003 			if (how != DELETE) {
2004 				frag = createDocumentFragment();
2005 			}
2006 
2007 			// If selection is empty, just return the fragment
2008 			if (self[START_OFFSET] == self[END_OFFSET]) {
2009 				return frag;
2010 			}
2011 
2012 			// Text node needs special case handling
2013 			if (self[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
2014 				// get the substring
2015 				s = self[START_CONTAINER].nodeValue;
2016 				sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
2017 
2018 				// set the original text node to its new value
2019 				if (how != CLONE) {
2020 					n = self[START_CONTAINER];
2021 					start = self[START_OFFSET];
2022 					len = self[END_OFFSET] - self[START_OFFSET];
2023 
2024 					if (start === 0 && len >= n.nodeValue.length - 1) {
2025 						n.parentNode.removeChild(n);
2026 					} else {
2027 						n.deleteData(start, len);
2028 					}
2029 
2030 					// Nothing is partially selected, so collapse to start point
2031 					self.collapse(TRUE);
2032 				}
2033 
2034 				if (how == DELETE) {
2035 					return;
2036 				}
2037 
2038 				if (sub.length > 0) {
2039 					frag.appendChild(doc.createTextNode(sub));
2040 				}
2041 
2042 				return frag;
2043 			}
2044 
2045 			// Copy nodes between the start/end offsets.
2046 			n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
2047 			cnt = self[END_OFFSET] - self[START_OFFSET];
2048 
2049 			while (n && cnt > 0) {
2050 				sibling = n.nextSibling;
2051 				xferNode = _traverseFullySelected(n, how);
2052 
2053 				if (frag) {
2054 					frag.appendChild(xferNode);
2055 				}
2056 
2057 				--cnt;
2058 				n = sibling;
2059 			}
2060 
2061 			// Nothing is partially selected, so collapse to start point
2062 			if (how != CLONE) {
2063 				self.collapse(TRUE);
2064 			}
2065 
2066 			return frag;
2067 		}
2068 
2069 		function _traverseCommonStartContainer(endAncestor, how) {
2070 			var frag, n, endIdx, cnt, sibling, xferNode;
2071 
2072 			if (how != DELETE) {
2073 				frag = createDocumentFragment();
2074 			}
2075 
2076 			n = _traverseRightBoundary(endAncestor, how);
2077 
2078 			if (frag) {
2079 				frag.appendChild(n);
2080 			}
2081 
2082 			endIdx = nodeIndex(endAncestor);
2083 			cnt = endIdx - self[START_OFFSET];
2084 
2085 			if (cnt <= 0) {
2086 				// Collapse to just before the endAncestor, which
2087 				// is partially selected.
2088 				if (how != CLONE) {
2089 					self.setEndBefore(endAncestor);
2090 					self.collapse(FALSE);
2091 				}
2092 
2093 				return frag;
2094 			}
2095 
2096 			n = endAncestor.previousSibling;
2097 			while (cnt > 0) {
2098 				sibling = n.previousSibling;
2099 				xferNode = _traverseFullySelected(n, how);
2100 
2101 				if (frag) {
2102 					frag.insertBefore(xferNode, frag.firstChild);
2103 				}
2104 
2105 				--cnt;
2106 				n = sibling;
2107 			}
2108 
2109 			// Collapse to just before the endAncestor, which
2110 			// is partially selected.
2111 			if (how != CLONE) {
2112 				self.setEndBefore(endAncestor);
2113 				self.collapse(FALSE);
2114 			}
2115 
2116 			return frag;
2117 		}
2118 
2119 		function _traverseCommonEndContainer(startAncestor, how) {
2120 			var frag, startIdx, n, cnt, sibling, xferNode;
2121 
2122 			if (how != DELETE) {
2123 				frag = createDocumentFragment();
2124 			}
2125 
2126 			n = _traverseLeftBoundary(startAncestor, how);
2127 			if (frag) {
2128 				frag.appendChild(n);
2129 			}
2130 
2131 			startIdx = nodeIndex(startAncestor);
2132 			++startIdx; // Because we already traversed it
2133 
2134 			cnt = self[END_OFFSET] - startIdx;
2135 			n = startAncestor.nextSibling;
2136 			while (n && cnt > 0) {
2137 				sibling = n.nextSibling;
2138 				xferNode = _traverseFullySelected(n, how);
2139 
2140 				if (frag) {
2141 					frag.appendChild(xferNode);
2142 				}
2143 
2144 				--cnt;
2145 				n = sibling;
2146 			}
2147 
2148 			if (how != CLONE) {
2149 				self.setStartAfter(startAncestor);
2150 				self.collapse(TRUE);
2151 			}
2152 
2153 			return frag;
2154 		}
2155 
2156 		function _traverseCommonAncestors(startAncestor, endAncestor, how) {
2157 			var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
2158 
2159 			if (how != DELETE) {
2160 				frag = createDocumentFragment();
2161 			}
2162 
2163 			n = _traverseLeftBoundary(startAncestor, how);
2164 			if (frag) {
2165 				frag.appendChild(n);
2166 			}
2167 
2168 			startOffset = nodeIndex(startAncestor);
2169 			endOffset = nodeIndex(endAncestor);
2170 			++startOffset;
2171 
2172 			cnt = endOffset - startOffset;
2173 			sibling = startAncestor.nextSibling;
2174 
2175 			while (cnt > 0) {
2176 				nextSibling = sibling.nextSibling;
2177 				n = _traverseFullySelected(sibling, how);
2178 
2179 				if (frag) {
2180 					frag.appendChild(n);
2181 				}
2182 
2183 				sibling = nextSibling;
2184 				--cnt;
2185 			}
2186 
2187 			n = _traverseRightBoundary(endAncestor, how);
2188 
2189 			if (frag) {
2190 				frag.appendChild(n);
2191 			}
2192 
2193 			if (how != CLONE) {
2194 				self.setStartAfter(startAncestor);
2195 				self.collapse(TRUE);
2196 			}
2197 
2198 			return frag;
2199 		}
2200 
2201 		function _traverseRightBoundary(root, how) {
2202 			var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
2203 			var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER];
2204 
2205 			if (next == root) {
2206 				return _traverseNode(next, isFullySelected, FALSE, how);
2207 			}
2208 
2209 			parent = next.parentNode;
2210 			clonedParent = _traverseNode(parent, FALSE, FALSE, how);
2211 
2212 			while (parent) {
2213 				while (next) {
2214 					prevSibling = next.previousSibling;
2215 					clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
2216 
2217 					if (how != DELETE) {
2218 						clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
2219 					}
2220 
2221 					isFullySelected = TRUE;
2222 					next = prevSibling;
2223 				}
2224 
2225 				if (parent == root) {
2226 					return clonedParent;
2227 				}
2228 
2229 				next = parent.previousSibling;
2230 				parent = parent.parentNode;
2231 
2232 				clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
2233 
2234 				if (how != DELETE) {
2235 					clonedGrandParent.appendChild(clonedParent);
2236 				}
2237 
2238 				clonedParent = clonedGrandParent;
2239 			}
2240 		}
2241 
2242 		function _traverseLeftBoundary(root, how) {
2243 			var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
2244 			var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
2245 
2246 			if (next == root) {
2247 				return _traverseNode(next, isFullySelected, TRUE, how);
2248 			}
2249 
2250 			parent = next.parentNode;
2251 			clonedParent = _traverseNode(parent, FALSE, TRUE, how);
2252 
2253 			while (parent) {
2254 				while (next) {
2255 					nextSibling = next.nextSibling;
2256 					clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
2257 
2258 					if (how != DELETE) {
2259 						clonedParent.appendChild(clonedChild);
2260 					}
2261 
2262 					isFullySelected = TRUE;
2263 					next = nextSibling;
2264 				}
2265 
2266 				if (parent == root) {
2267 					return clonedParent;
2268 				}
2269 
2270 				next = parent.nextSibling;
2271 				parent = parent.parentNode;
2272 
2273 				clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
2274 
2275 				if (how != DELETE) {
2276 					clonedGrandParent.appendChild(clonedParent);
2277 				}
2278 
2279 				clonedParent = clonedGrandParent;
2280 			}
2281 		}
2282 
2283 		function _traverseNode(n, isFullySelected, isLeft, how) {
2284 			var txtValue, newNodeValue, oldNodeValue, offset, newNode;
2285 
2286 			if (isFullySelected) {
2287 				return _traverseFullySelected(n, how);
2288 			}
2289 
2290 			if (n.nodeType == 3 /* TEXT_NODE */) {
2291 				txtValue = n.nodeValue;
2292 
2293 				if (isLeft) {
2294 					offset = self[START_OFFSET];
2295 					newNodeValue = txtValue.substring(offset);
2296 					oldNodeValue = txtValue.substring(0, offset);
2297 				} else {
2298 					offset = self[END_OFFSET];
2299 					newNodeValue = txtValue.substring(0, offset);
2300 					oldNodeValue = txtValue.substring(offset);
2301 				}
2302 
2303 				if (how != CLONE) {
2304 					n.nodeValue = oldNodeValue;
2305 				}
2306 
2307 				if (how == DELETE) {
2308 					return;
2309 				}
2310 
2311 				newNode = dom.clone(n, FALSE);
2312 				newNode.nodeValue = newNodeValue;
2313 
2314 				return newNode;
2315 			}
2316 
2317 			if (how == DELETE) {
2318 				return;
2319 			}
2320 
2321 			return dom.clone(n, FALSE);
2322 		}
2323 
2324 		function _traverseFullySelected(n, how) {
2325 			if (how != DELETE) {
2326 				return how == CLONE ? dom.clone(n, TRUE) : n;
2327 			}
2328 
2329 			n.parentNode.removeChild(n);
2330 		}
2331 
2332 		function toStringIE() {
2333 			return dom.create('body', null, cloneContents()).outerText;
2334 		}
2335 
2336 		extend(self, {
2337 			// Inital states
2338 			startContainer: doc,
2339 			startOffset: 0,
2340 			endContainer: doc,
2341 			endOffset: 0,
2342 			collapsed: TRUE,
2343 			commonAncestorContainer: doc,
2344 
2345 			// Range constants
2346 			START_TO_START: 0,
2347 			START_TO_END: 1,
2348 			END_TO_END: 2,
2349 			END_TO_START: 3,
2350 
2351 			// Public methods
2352 			setStart: setStart,
2353 			setEnd: setEnd,
2354 			setStartBefore: setStartBefore,
2355 			setStartAfter: setStartAfter,
2356 			setEndBefore: setEndBefore,
2357 			setEndAfter: setEndAfter,
2358 			collapse: collapse,
2359 			selectNode: selectNode,
2360 			selectNodeContents: selectNodeContents,
2361 			compareBoundaryPoints: compareBoundaryPoints,
2362 			deleteContents: deleteContents,
2363 			extractContents: extractContents,
2364 			cloneContents: cloneContents,
2365 			insertNode: insertNode,
2366 			surroundContents: surroundContents,
2367 			cloneRange: cloneRange,
2368 			toStringIE: toStringIE
2369 		});
2370 
2371 		return self;
2372 	}
2373 
2374 	// Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
2375 	Range.prototype.toString = function() {
2376 		return this.toStringIE();
2377 	};
2378 
2379 	return Range;
2380 });
2381 
2382 // Included from: js/tinymce/classes/html/Entities.js
2383 
2384 /**
2385  * Entities.js
2386  *
2387  * Copyright, Moxiecode Systems AB
2388  * Released under LGPL License.
2389  *
2390  * License: http://www.tinymce.com/license
2391  * Contributing: http://www.tinymce.com/contributing
2392  */
2393 
2394 /*jshint bitwise:false */
2395 /*eslint no-bitwise:0 */
2396 
2397 /**
2398  * Entity encoder class.
2399  *
2400  * @class tinymce.html.Entities
2401  * @static
2402  * @version 3.4
2403  */
2404 define("tinymce/html/Entities", [
2405 	"tinymce/util/Tools"
2406 ], function(Tools) {
2407 	var makeMap = Tools.makeMap;
2408 
2409 	var namedEntities, baseEntities, reverseEntities,
2410 		attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
2411 		textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
2412 		rawCharsRegExp = /[<>&\"\']/g,
2413 		entityRegExp = /&(#x|#)?([\w]+);/g,
2414 		asciiMap = {
2415 			128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
2416 			135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
2417 			142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
2418 			150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
2419 			156: "\u0153", 158: "\u017E", 159: "\u0178"
2420 		};
2421 
2422 	// Raw entities
2423 	baseEntities = {
2424 		'\"': '"', // Needs to be escaped since the YUI compressor would otherwise break the code
2425 		"'": ''',
2426 		'<': '<',
2427 		'>': '>',
2428 		'&': '&',
2429 		'\u0060': '`'
2430 	};
2431 
2432 	// Reverse lookup table for raw entities
2433 	reverseEntities = {
2434 		'<': '<',
2435 		'>': '>',
2436 		'&': '&',
2437 		'"': '"',
2438 		''': "'"
2439 	};
2440 
2441 	// Decodes text by using the browser
2442 	function nativeDecode(text) {
2443 		var elm;
2444 
2445 		elm = document.createElement("div");
2446 		elm.innerHTML = text;
2447 
2448 		return elm.textContent || elm.innerText || text;
2449 	}
2450 
2451 	// Build a two way lookup table for the entities
2452 	function buildEntitiesLookup(items, radix) {
2453 		var i, chr, entity, lookup = {};
2454 
2455 		if (items) {
2456 			items = items.split(',');
2457 			radix = radix || 10;
2458 
2459 			// Build entities lookup table
2460 			for (i = 0; i < items.length; i += 2) {
2461 				chr = String.fromCharCode(parseInt(items[i], radix));
2462 
2463 				// Only add non base entities
2464 				if (!baseEntities[chr]) {
2465 					entity = '&' + items[i + 1] + ';';
2466 					lookup[chr] = entity;
2467 					lookup[entity] = chr;
2468 				}
2469 			}
2470 
2471 			return lookup;
2472 		}
2473 	}
2474 
2475 	// Unpack entities lookup where the numbers are in radix 32 to reduce the size
2476 	namedEntities = buildEntitiesLookup(
2477 		'50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
2478 		'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
2479 		'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
2480 		'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
2481 		'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
2482 		'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
2483 		'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
2484 		'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
2485 		'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
2486 		'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
2487 		'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
2488 		'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
2489 		't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
2490 		'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
2491 		'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
2492 		'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
2493 		'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
2494 		'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
2495 		'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
2496 		'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
2497 		'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
2498 		'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
2499 		'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
2500 		'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
2501 		'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
2502 
2503 	var Entities = {
2504 		/**
2505 		 * Encodes the specified string using raw entities. This means only the required XML base entities will be endoded.
2506 		 *
2507 		 * @method encodeRaw
2508 		 * @param {String} text Text to encode.
2509 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
2510 		 * @return {String} Entity encoded text.
2511 		 */
2512 		encodeRaw: function(text, attr) {
2513 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2514 				return baseEntities[chr] || chr;
2515 			});
2516 		},
2517 
2518 		/**
2519 		 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
2520 		 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
2521 		 * and is exposed as the DOMUtils.encode function.
2522 		 *
2523 		 * @method encodeAllRaw
2524 		 * @param {String} text Text to encode.
2525 		 * @return {String} Entity encoded text.
2526 		 */
2527 		encodeAllRaw: function(text) {
2528 			return ('' + text).replace(rawCharsRegExp, function(chr) {
2529 				return baseEntities[chr] || chr;
2530 			});
2531 		},
2532 
2533 		/**
2534 		 * Encodes the specified string using numeric entities. The core entities will be
2535 		 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
2536 		 *
2537 		 * @method encodeNumeric
2538 		 * @param {String} text Text to encode.
2539 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
2540 		 * @return {String} Entity encoded text.
2541 		 */
2542 		encodeNumeric: function(text, attr) {
2543 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2544 				// Multi byte sequence convert it to a single entity
2545 				if (chr.length > 1) {
2546 					return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
2547 				}
2548 
2549 				return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
2550 			});
2551 		},
2552 
2553 		/**
2554 		 * Encodes the specified string using named entities. The core entities will be encoded
2555 		 * as named ones but all non lower ascii characters will be encoded into named entities.
2556 		 *
2557 		 * @method encodeNamed
2558 		 * @param {String} text Text to encode.
2559 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
2560 		 * @param {Object} entities Optional parameter with entities to use.
2561 		 * @return {String} Entity encoded text.
2562 		 */
2563 		encodeNamed: function(text, attr, entities) {
2564 			entities = entities || namedEntities;
2565 
2566 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2567 				return baseEntities[chr] || entities[chr] || chr;
2568 			});
2569 		},
2570 
2571 		/**
2572 		 * Returns an encode function based on the name(s) and it's optional entities.
2573 		 *
2574 		 * @method getEncodeFunc
2575 		 * @param {String} name Comma separated list of encoders for example named,numeric.
2576 		 * @param {String} entities Optional parameter with entities to use instead of the built in set.
2577 		 * @return {function} Encode function to be used.
2578 		 */
2579 		getEncodeFunc: function(name, entities) {
2580 			entities = buildEntitiesLookup(entities) || namedEntities;
2581 
2582 			function encodeNamedAndNumeric(text, attr) {
2583 				return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2584 					return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
2585 				});
2586 			}
2587 
2588 			function encodeCustomNamed(text, attr) {
2589 				return Entities.encodeNamed(text, attr, entities);
2590 			}
2591 
2592 			// Replace + with , to be compatible with previous TinyMCE versions
2593 			name = makeMap(name.replace(/\+/g, ','));
2594 
2595 			// Named and numeric encoder
2596 			if (name.named && name.numeric) {
2597 				return encodeNamedAndNumeric;
2598 			}
2599 
2600 			// Named encoder
2601 			if (name.named) {
2602 				// Custom names
2603 				if (entities) {
2604 					return encodeCustomNamed;
2605 				}
2606 
2607 				return Entities.encodeNamed;
2608 			}
2609 
2610 			// Numeric
2611 			if (name.numeric) {
2612 				return Entities.encodeNumeric;
2613 			}
2614 
2615 			// Raw encoder
2616 			return Entities.encodeRaw;
2617 		},
2618 
2619 		/**
2620 		 * Decodes the specified string, this will replace entities with raw UTF characters.
2621 		 *
2622 		 * @method decode
2623 		 * @param {String} text Text to entity decode.
2624 		 * @return {String} Entity decoded string.
2625 		 */
2626 		decode: function(text) {
2627 			return text.replace(entityRegExp, function(all, numeric, value) {
2628 				if (numeric) {
2629 					value = parseInt(value, numeric.length === 2 ? 16 : 10);
2630 
2631 					// Support upper UTF
2632 					if (value > 0xFFFF) {
2633 						value -= 0x10000;
2634 
2635 						return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));
2636 					} else {
2637 						return asciiMap[value] || String.fromCharCode(value);
2638 					}
2639 				}
2640 
2641 				return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
2642 			});
2643 		}
2644 	};
2645 
2646 	return Entities;
2647 });
2648 
2649 // Included from: js/tinymce/classes/Env.js
2650 
2651 /**
2652  * Env.js
2653  *
2654  * Copyright, Moxiecode Systems AB
2655  * Released under LGPL License.
2656  *
2657  * License: http://www.tinymce.com/license
2658  * Contributing: http://www.tinymce.com/contributing
2659  */
2660 
2661 /**
2662  * This class contains various environment constants like browser versions etc.
2663  * Normally you don't want to sniff specific browser versions but sometimes you have
2664  * to when it's impossible to feature detect. So use this with care.
2665  *
2666  * @class tinymce.Env
2667  * @static
2668  */
2669 define("tinymce/Env", [], function() {
2670 	var nav = navigator, userAgent = nav.userAgent;
2671 	var opera, webkit, ie, ie11, gecko, mac, iDevice;
2672 
2673 	opera = window.opera && window.opera.buildNumber;
2674 	webkit = /WebKit/.test(userAgent);
2675 	ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
2676 	ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
2677 	ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
2678 	ie = ie || ie11;
2679 	gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
2680 	mac = userAgent.indexOf('Mac') != -1;
2681 	iDevice = /(iPad|iPhone)/.test(userAgent);
2682 
2683 	// Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
2684 	// says it has contentEditable support but there is no visible caret.
2685 	var contentEditable = !iDevice || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
2686 
2687 	return {
2688 		/**
2689 		 * Constant that is true if the browser is Opera.
2690 		 *
2691 		 * @property opera
2692 		 * @type Boolean
2693 		 * @final
2694 		 */
2695 		opera: opera,
2696 
2697 		/**
2698 		 * Constant that is true if the browser is WebKit (Safari/Chrome).
2699 		 *
2700 		 * @property webKit
2701 		 * @type Boolean
2702 		 * @final
2703 		 */
2704 		webkit: webkit,
2705 
2706 		/**
2707 		 * Constant that is more than zero if the browser is IE.
2708 		 *
2709 		 * @property ie
2710 		 * @type Boolean
2711 		 * @final
2712 		 */
2713 		ie: ie,
2714 
2715 		/**
2716 		 * Constant that is true if the browser is Gecko.
2717 		 *
2718 		 * @property gecko
2719 		 * @type Boolean
2720 		 * @final
2721 		 */
2722 		gecko: gecko,
2723 
2724 		/**
2725 		 * Constant that is true if the os is Mac OS.
2726 		 *
2727 		 * @property mac
2728 		 * @type Boolean
2729 		 * @final
2730 		 */
2731 		mac: mac,
2732 
2733 		/**
2734 		 * Constant that is true if the os is iOS.
2735 		 *
2736 		 * @property iOS
2737 		 * @type Boolean
2738 		 * @final
2739 		 */
2740 		iOS: iDevice,
2741 
2742 		/**
2743 		 * Constant that is true if the browser supports editing.
2744 		 *
2745 		 * @property contentEditable
2746 		 * @type Boolean
2747 		 * @final
2748 		 */
2749 		contentEditable: contentEditable,
2750 
2751 		/**
2752 		 * Transparent image data url.
2753 		 *
2754 		 * @property transparentSrc
2755 		 * @type Boolean
2756 		 * @final
2757 		 */
2758 		transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
2759 
2760 		/**
2761 		 * Returns true/false if the browser can or can't place the caret after a inline block like an image.
2762 		 *
2763 		 * @property noCaretAfter
2764 		 * @type Boolean
2765 		 * @final
2766 		 */
2767 		caretAfter: ie != 8,
2768 
2769 		/**
2770 		 * Constant that is true if the browser supports native DOM Ranges. IE 9+.
2771 		 *
2772 		 * @property range
2773 		 * @type Boolean
2774 		 */
2775 		range: window.getSelection && "Range" in window,
2776 
2777 		/**
2778 		 * Returns the IE document mode for non IE browsers this will fake IE 10.
2779 		 *
2780 		 * @property documentMode
2781 		 * @type Number
2782 		 */
2783 		documentMode: ie ? (document.documentMode || 7) : 10
2784 	};
2785 });
2786 
2787 // Included from: js/tinymce/classes/dom/StyleSheetLoader.js
2788 
2789 /**
2790  * StyleSheetLoader.js
2791  *
2792  * Copyright, Moxiecode Systems AB
2793  * Released under LGPL License.
2794  *
2795  * License: http://www.tinymce.com/license
2796  * Contributing: http://www.tinymce.com/contributing
2797  */
2798 
2799 /**
2800  * This class handles loading of external stylesheets and fires events when these are loaded.
2801  *
2802  * @class tinymce.dom.StyleSheetLoader
2803  * @private
2804  */
2805 define("tinymce/dom/StyleSheetLoader", [], function() {
2806 	"use strict";
2807 
2808 	return function(document, settings) {
2809 		var idCount = 0, loadedStates = {}, maxLoadTime;
2810 
2811 		settings = settings || {};
2812 		maxLoadTime = settings.maxLoadTime || 5000;
2813 
2814 		function appendToHead(node) {
2815 			document.getElementsByTagName('head')[0].appendChild(node);
2816 		}
2817 
2818 		/**
2819 		 * Loads the specified css style sheet file and call the loadedCallback once it's finished loading.
2820 		 *
2821 		 * @method load
2822 		 * @param {String} url Url to be loaded.
2823 		 * @param {Function} loadedCallback Callback to be executed when loaded.
2824 		 * @param {Function} errorCallback Callback to be executed when failed loading.
2825 		 */
2826 		function load(url, loadedCallback, errorCallback) {
2827 			var link, style, startTime, state;
2828 
2829 			function passed() {
2830 				var callbacks = state.passed, i = callbacks.length;
2831 
2832 				while (i--) {
2833 					callbacks[i]();
2834 				}
2835 
2836 				state.status = 2;
2837 				state.passed = [];
2838 				state.failed = [];
2839 			}
2840 
2841 			function failed() {
2842 				var callbacks = state.failed, i = callbacks.length;
2843 
2844 				while (i--) {
2845 					callbacks[i]();
2846 				}
2847 
2848 				state.status = 3;
2849 				state.passed = [];
2850 				state.failed = [];
2851 			}
2852 
2853 			// Sniffs for older WebKit versions that have the link.onload but a broken one
2854 			function isOldWebKit() {
2855 				var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/);
2856 				return !!(webKitChunks && webKitChunks[1] < 536);
2857 			}
2858 
2859 			// Calls the waitCallback until the test returns true or the timeout occurs
2860 			function wait(testCallback, waitCallback) {
2861 				if (!testCallback()) {
2862 					// Wait for timeout
2863 					if ((new Date().getTime()) - startTime < maxLoadTime) {
2864 						window.setTimeout(waitCallback, 0);
2865 					} else {
2866 						failed();
2867 					}
2868 				}
2869 			}
2870 
2871 			// Workaround for WebKit that doesn't properly support the onload event for link elements
2872 			// Or WebKit that fires the onload event before the StyleSheet is added to the document
2873 			function waitForWebKitLinkLoaded() {
2874 				wait(function() {
2875 					var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner;
2876 
2877 					while (i--) {
2878 						styleSheet = styleSheets[i];
2879 						owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement;
2880 						if (owner && owner.id === link.id) {
2881 							passed();
2882 							return true;
2883 						}
2884 					}
2885 				}, waitForWebKitLinkLoaded);
2886 			}
2887 
2888 			// Workaround for older Geckos that doesn't have any onload event for StyleSheets
2889 			function waitForGeckoLinkLoaded() {
2890 				wait(function() {
2891 					try {
2892 						// Accessing the cssRules will throw an exception until the CSS file is loaded
2893 						var cssRules = style.sheet.cssRules;
2894 						passed();
2895 						return !!cssRules;
2896 					} catch (ex) {
2897 						// Ignore
2898 					}
2899 				}, waitForGeckoLinkLoaded);
2900 			}
2901 
2902 			if (!loadedStates[url]) {
2903 				state = {
2904 					passed: [],
2905 					failed: []
2906 				};
2907 
2908 				loadedStates[url] = state;
2909 			} else {
2910 				state = loadedStates[url];
2911 			}
2912 
2913 			if (loadedCallback) {
2914 				state.passed.push(loadedCallback);
2915 			}
2916 
2917 			if (errorCallback) {
2918 				state.failed.push(errorCallback);
2919 			}
2920 
2921 			// Is loading wait for it to pass
2922 			if (state.status == 1) {
2923 				return;
2924 			}
2925 
2926 			// Has finished loading and was success
2927 			if (state.status == 2) {
2928 				passed();
2929 				return;
2930 			}
2931 
2932 			// Has finished loading and was a failure
2933 			if (state.status == 3) {
2934 				failed();
2935 				return;
2936 			}
2937 
2938 			// Start loading
2939 			state.status = 1;
2940 			link = document.createElement('link');
2941 			link.rel = 'stylesheet';
2942 			link.type = 'text/css';
2943 			link.id = 'u' + (idCount++);
2944 			link.async = false;
2945 			link.defer = false;
2946 			startTime = new Date().getTime();
2947 
2948 			// Feature detect onload on link element and sniff older webkits since it has an broken onload event
2949 			if ("onload" in link && !isOldWebKit()) {
2950 				link.onload = waitForWebKitLinkLoaded;
2951 				link.onerror = failed;
2952 			} else {
2953 				// Sniff for old Firefox that doesn't support the onload event on link elements
2954 				// TODO: Remove this in the future when everyone uses modern browsers
2955 				if (navigator.userAgent.indexOf("Firefox") > 0) {
2956 					style = document.createElement('style');
2957 					style.textContent = '@import "' + url + '"';
2958 					waitForGeckoLinkLoaded();
2959 					appendToHead(style);
2960 					return;
2961 				} else {
2962 					// Use the id owner on older webkits
2963 					waitForWebKitLinkLoaded();
2964 				}
2965 			}
2966 
2967 			appendToHead(link);
2968 			link.href = url;
2969 		}
2970 
2971 		this.load = load;
2972 	};
2973 });
2974 
2975 // Included from: js/tinymce/classes/dom/DOMUtils.js
2976 
2977 /**
2978  * DOMUtils.js
2979  *
2980  * Copyright, Moxiecode Systems AB
2981  * Released under LGPL License.
2982  *
2983  * License: http://www.tinymce.com/license
2984  * Contributing: http://www.tinymce.com/contributing
2985  */
2986 
2987 /**
2988  * Utility class for various DOM manipulation and retrieval functions.
2989  *
2990  * @class tinymce.dom.DOMUtils
2991  * @example
2992  * // Add a class to an element by id in the page
2993  * tinymce.DOM.addClass('someid', 'someclass');
2994  *
2995  * // Add a class to an element by id inside the editor
2996  * tinymce.activeEditor.dom.addClass('someid', 'someclass');
2997  */
2998 define("tinymce/dom/DOMUtils", [
2999 	"tinymce/dom/Sizzle",
3000 	"tinymce/html/Styles",
3001 	"tinymce/dom/EventUtils",
3002 	"tinymce/dom/TreeWalker",
3003 	"tinymce/dom/Range",
3004 	"tinymce/html/Entities",
3005 	"tinymce/Env",
3006 	"tinymce/util/Tools",
3007 	"tinymce/dom/StyleSheetLoader"
3008 ], function(Sizzle, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools, StyleSheetLoader) {
3009 	// Shorten names
3010 	var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim, extend = Tools.extend;
3011 	var isWebKit = Env.webkit, isIE = Env.ie;
3012 	var simpleSelectorRe = /^([a-z0-9],?)+$/i;
3013 	var whiteSpaceRegExp = /^[ \t\r\n]*$/;
3014 	var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
3015 
3016 	/**
3017 	 * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
3018 	 *
3019 	 * @constructor
3020 	 * @method DOMUtils
3021 	 * @param {Document} d Document reference to bind the utility class to.
3022 	 * @param {settings} s Optional settings collection.
3023 	 */
3024 	function DOMUtils(doc, settings) {
3025 		var self = this, blockElementsMap;
3026 
3027 		self.doc = doc;
3028 		self.win = window;
3029 		self.files = {};
3030 		self.counter = 0;
3031 		self.stdMode = !isIE || doc.documentMode >= 8;
3032 		self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
3033 		self.hasOuterHTML = "outerHTML" in doc.createElement("a");
3034 		self.styleSheetLoader = new StyleSheetLoader(doc);
3035 		this.boundEvents = [];
3036 
3037 		self.settings = settings = extend({
3038 			keep_values: false,
3039 			hex_colors: 1
3040 		}, settings);
3041 
3042 		self.schema = settings.schema;
3043 		self.styles = new Styles({
3044 			url_converter: settings.url_converter,
3045 			url_converter_scope: settings.url_converter_scope
3046 		}, settings.schema);
3047 
3048 		self.fixDoc(doc);
3049 		self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
3050 		blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
3051 
3052 		/**
3053 		 * Returns true/false if the specified element is a block element or not.
3054 		 *
3055 		 * @method isBlock
3056 		 * @param {Node/String} node Element/Node to check.
3057 		 * @return {Boolean} True/False state if the node is a block element or not.
3058 		 */
3059 		self.isBlock = function(node) {
3060 			// Fix for #5446
3061 			if (!node) {
3062 				return false;
3063 			}
3064 
3065 			// This function is called in module pattern style since it might be executed with the wrong this scope
3066 			var type = node.nodeType;
3067 
3068 			// If it's a node then check the type and use the nodeName
3069 			if (type) {
3070 				return !!(type === 1 && blockElementsMap[node.nodeName]);
3071 			}
3072 
3073 			return !!blockElementsMap[node];
3074 		};
3075 	}
3076 
3077 	DOMUtils.prototype = {
3078 		root: null,
3079 		props: {
3080 			"for": "htmlFor",
3081 			"class": "className",
3082 			className: "className",
3083 			checked: "checked",
3084 			disabled: "disabled",
3085 			maxlength: "maxLength",
3086 			readonly: "readOnly",
3087 			selected: "selected",
3088 			value: "value",
3089 			id: "id",
3090 			name: "name",
3091 			type: "type"
3092 		},
3093 
3094 		fixDoc: function(doc) {
3095 			var settings = this.settings, name;
3096 
3097 			if (isIE && settings.schema) {
3098 				// Add missing HTML 4/5 elements to IE
3099 				('abbr article aside audio canvas ' +
3100 				'details figcaption figure footer ' +
3101 				'header hgroup mark menu meter nav ' +
3102 				'output progress section summary ' +
3103 				'time video').replace(/\w+/g, function(name) {
3104 					doc.createElement(name);
3105 				});
3106 
3107 				// Create all custom elements
3108 				for (name in settings.schema.getCustomElements()) {
3109 					doc.createElement(name);
3110 				}
3111 			}
3112 		},
3113 
3114 		clone: function(node, deep) {
3115 			var self = this, clone, doc;
3116 
3117 			// TODO: Add feature detection here in the future
3118 			if (!isIE || node.nodeType !== 1 || deep) {
3119 				return node.cloneNode(deep);
3120 			}
3121 
3122 			doc = self.doc;
3123 
3124 			// Make a HTML5 safe shallow copy
3125 			if (!deep) {
3126 				clone = doc.createElement(node.nodeName);
3127 
3128 				// Copy attribs
3129 				each(self.getAttribs(node), function(attr) {
3130 					self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
3131 				});
3132 
3133 				return clone;
3134 			}
3135 /*
3136 			// Setup HTML5 patched document fragment
3137 			if (!self.frag) {
3138 				self.frag = doc.createDocumentFragment();
3139 				self.fixDoc(self.frag);
3140 			}
3141 
3142 			// Make a deep copy by adding it to the document fragment then removing it this removed the :section
3143 			clone = doc.createElement('div');
3144 			self.frag.appendChild(clone);
3145 			clone.innerHTML = node.outerHTML;
3146 			self.frag.removeChild(clone);
3147 */
3148 			return clone.firstChild;
3149 		},
3150 
3151 		/**
3152 		 * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
3153 		 * go above the point of this root node.
3154 		 *
3155 		 * @method getRoot
3156 		 * @return {Element} Root element for the utility class.
3157 		 */
3158 		getRoot: function() {
3159 			var self = this;
3160 
3161 			return self.get(self.settings.root_element) || self.doc.body;
3162 		},
3163 
3164 		/**
3165 		 * Returns the viewport of the window.
3166 		 *
3167 		 * @method getViewPort
3168 		 * @param {Window} win Optional window to get viewport of.
3169 		 * @return {Object} Viewport object with fields x, y, w and h.
3170 		 */
3171 		getViewPort: function(win) {
3172 			var doc, rootElm;
3173 
3174 			win = !win ? this.win : win;
3175 			doc = win.document;
3176 			rootElm = this.boxModel ? doc.documentElement : doc.body;
3177 
3178 			// Returns viewport size excluding scrollbars
3179 			return {
3180 				x: win.pageXOffset || rootElm.scrollLeft,
3181 				y: win.pageYOffset || rootElm.scrollTop,
3182 				w: win.innerWidth || rootElm.clientWidth,
3183 				h: win.innerHeight || rootElm.clientHeight
3184 			};
3185 		},
3186 
3187 		/**
3188 		 * Returns the rectangle for a specific element.
3189 		 *
3190 		 * @method getRect
3191 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
3192 		 * @return {object} Rectangle for specified element object with x, y, w, h fields.
3193 		 */
3194 		getRect: function(elm) {
3195 			var self = this, pos, size;
3196 
3197 			elm = self.get(elm);
3198 			pos = self.getPos(elm);
3199 			size = self.getSize(elm);
3200 
3201 			return {
3202 				x: pos.x, y: pos.y,
3203 				w: size.w, h: size.h
3204 			};
3205 		},
3206 
3207 		/**
3208 		 * Returns the size dimensions of the specified element.
3209 		 *
3210 		 * @method getSize
3211 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
3212 		 * @return {object} Rectangle for specified element object with w, h fields.
3213 		 */
3214 		getSize: function(elm) {
3215 			var self = this, w, h;
3216 
3217 			elm = self.get(elm);
3218 			w = self.getStyle(elm, 'width');
3219 			h = self.getStyle(elm, 'height');
3220 
3221 			// Non pixel value, then force offset/clientWidth
3222 			if (w.indexOf('px') === -1) {
3223 				w = 0;
3224 			}
3225 
3226 			// Non pixel value, then force offset/clientWidth
3227 			if (h.indexOf('px') === -1) {
3228 				h = 0;
3229 			}
3230 
3231 			return {
3232 				w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
3233 				h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
3234 			};
3235 		},
3236 
3237 		/**
3238 		 * Returns a node by the specified selector function. This function will
3239 		 * loop through all parent nodes and call the specified function for each node.
3240 		 * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
3241 		 * and the node it found will be returned.
3242 		 *
3243 		 * @method getParent
3244 		 * @param {Node/String} node DOM node to search parents on or ID string.
3245 		 * @param {function} selector Selection function or CSS selector to execute on each node.
3246 		 * @param {Node} root Optional root element, never go below this point.
3247 		 * @return {Node} DOM Node or null if it wasn't found.
3248 		 */
3249 		getParent: function(node, selector, root) {
3250 			return this.getParents(node, selector, root, false);
3251 		},
3252 
3253 		/**
3254 		 * Returns a node list of all parents matching the specified selector function or pattern.
3255 		 * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
3256 		 *
3257 		 * @method getParents
3258 		 * @param {Node/String} node DOM node to search parents on or ID string.
3259 		 * @param {function} selector Selection function to execute on each node or CSS pattern.
3260 		 * @param {Node} root Optional root element, never go below this point.
3261 		 * @return {Array} Array of nodes or null if it wasn't found.
3262 		 */
3263 		getParents: function(node, selector, root, collect) {
3264 			var self = this, selectorVal, result = [];
3265 
3266 			node = self.get(node);
3267 			collect = collect === undefined;
3268 
3269 			// Default root on inline mode
3270 			root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
3271 
3272 			// Wrap node name as func
3273 			if (is(selector, 'string')) {
3274 				selectorVal = selector;
3275 
3276 				if (selector === '*') {
3277 					selector = function(node) {return node.nodeType == 1;};
3278 				} else {
3279 					selector = function(node) {
3280 						return self.is(node, selectorVal);
3281 					};
3282 				}
3283 			}
3284 
3285 			while (node) {
3286 				if (node == root || !node.nodeType || node.nodeType === 9) {
3287 					break;
3288 				}
3289 
3290 				if (!selector || selector(node)) {
3291 					if (collect) {
3292 						result.push(node);
3293 					} else {
3294 						return node;
3295 					}
3296 				}
3297 
3298 				node = node.parentNode;
3299 			}
3300 
3301 			return collect ? result : null;
3302 		},
3303 
3304 		/**
3305 		 * Returns the specified element by ID or the input element if it isn't a string.
3306 		 *
3307 		 * @method get
3308 		 * @param {String/Element} n Element id to look for or element to just pass though.
3309 		 * @return {Element} Element matching the specified id or null if it wasn't found.
3310 		 */
3311 		get: function(elm) {
3312 			var name;
3313 
3314 			if (elm && this.doc && typeof(elm) == 'string') {
3315 				name = elm;
3316 				elm = this.doc.getElementById(elm);
3317 
3318 				// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
3319 				if (elm && elm.id !== name) {
3320 					return this.doc.getElementsByName(name)[1];
3321 				}
3322 			}
3323 
3324 			return elm;
3325 		},
3326 
3327 		/**
3328 		 * Returns the next node that matches selector or function
3329 		 *
3330 		 * @method getNext
3331 		 * @param {Node} node Node to find siblings from.
3332 		 * @param {String/function} selector Selector CSS expression or function.
3333 		 * @return {Node} Next node item matching the selector or null if it wasn't found.
3334 		 */
3335 		getNext: function(node, selector) {
3336 			return this._findSib(node, selector, 'nextSibling');
3337 		},
3338 
3339 		/**
3340 		 * Returns the previous node that matches selector or function
3341 		 *
3342 		 * @method getPrev
3343 		 * @param {Node} node Node to find siblings from.
3344 		 * @param {String/function} selector Selector CSS expression or function.
3345 		 * @return {Node} Previous node item matching the selector or null if it wasn't found.
3346 		 */
3347 		getPrev: function(node, selector) {
3348 			return this._findSib(node, selector, 'previousSibling');
3349 		},
3350 
3351 		// #ifndef jquery
3352 
3353 		/**
3354 		 * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
3355 		 * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
3356 		 * on more complex patterns.
3357 		 *
3358 		 * @method select
3359 		 * @param {String} selector CSS level 3 pattern to select/find elements by.
3360 		 * @param {Object} scope Optional root element/scope element to search in.
3361 		 * @return {Array} Array with all matched elements.
3362 		 * @example
3363 		 * // Adds a class to all paragraphs in the currently active editor
3364 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
3365 		 *
3366 		 * // Adds a class to all spans that have the test class in the currently active editor
3367 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
3368 		 */
3369 		select: function(selector, scope) {
3370 			var self = this;
3371 
3372 			//Sizzle.selectors.cacheLength = 0;
3373 			return Sizzle(selector, self.get(scope) || self.get(self.settings.root_element) || self.doc, []);
3374 		},
3375 
3376 		/**
3377 		 * Returns true/false if the specified element matches the specified css pattern.
3378 		 *
3379 		 * @method is
3380 		 * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
3381 		 * @param {String} selector CSS pattern to match the element against.
3382 		 */
3383 		is: function(elm, selector) {
3384 			var i;
3385 
3386 			// If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
3387 			if (elm.length === undefined) {
3388 				// Simple all selector
3389 				if (selector === '*') {
3390 					return elm.nodeType == 1;
3391 				}
3392 
3393 				// Simple selector just elements
3394 				if (simpleSelectorRe.test(selector)) {
3395 					selector = selector.toLowerCase().split(/,/);
3396 					elm = elm.nodeName.toLowerCase();
3397 
3398 					for (i = selector.length - 1; i >= 0; i--) {
3399 						if (selector[i] == elm) {
3400 							return true;
3401 						}
3402 					}
3403 
3404 					return false;
3405 				}
3406 			}
3407 
3408 			// Is non element
3409 			if (elm.nodeType && elm.nodeType != 1) {
3410 				return false;
3411 			}
3412 
3413 			var elms = elm.nodeType ? [elm] : elm;
3414 			return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0;
3415 		},
3416 
3417 		// #endif
3418 
3419 		/**
3420 		 * Adds the specified element to another element or elements.
3421 		 *
3422 		 * @method add
3423 		 * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
3424 		 * @param {String/Element} name Name of new element to add or existing element to add.
3425 		 * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
3426 		 * @param {String} html Optional inner HTML contents to add for each element.
3427 		 * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
3428 		 * were passed in.
3429 		 * @example
3430 		 * // Adds a new paragraph to the end of the active editor
3431 		 * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
3432 		 */
3433 		add: function(parentElm, name, attrs, html, create) {
3434 			var self = this;
3435 
3436 			return this.run(parentElm, function(parentElm) {
3437 				var newElm;
3438 
3439 				newElm = is(name, 'string') ? self.doc.createElement(name) : name;
3440 				self.setAttribs(newElm, attrs);
3441 
3442 				if (html) {
3443 					if (html.nodeType) {
3444 						newElm.appendChild(html);
3445 					} else {
3446 						self.setHTML(newElm, html);
3447 					}
3448 				}
3449 
3450 				return !create ? parentElm.appendChild(newElm) : newElm;
3451 			});
3452 		},
3453 
3454 		/**
3455 		 * Creates a new element.
3456 		 *
3457 		 * @method create
3458 		 * @param {String} name Name of new element.
3459 		 * @param {Object} attrs Optional object name/value collection with element attributes.
3460 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
3461 		 * @return {Element} HTML DOM node element that got created.
3462 		 * @example
3463 		 * // Adds an element where the caret/selection is in the active editor
3464 		 * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
3465 		 * tinymce.activeEditor.selection.setNode(el);
3466 		 */
3467 		create: function(name, attrs, html) {
3468 			return this.add(this.doc.createElement(name), name, attrs, html, 1);
3469 		},
3470 
3471 		/**
3472 		 * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
3473 		 *
3474 		 * @method createHTML
3475 		 * @param {String} name Name of new element.
3476 		 * @param {Object} attrs Optional object name/value collection with element attributes.
3477 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
3478 		 * @return {String} String with new HTML element, for example: <a href="#">test</a>.
3479 		 * @example
3480 		 * // Creates a html chunk and inserts it at the current selection/caret location
3481 		 * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
3482 		 */
3483 		createHTML: function(name, attrs, html) {
3484 			var outHtml = '', key;
3485 
3486 			outHtml += '<' + name;
3487 
3488 			for (key in attrs) {
3489 				if (attrs.hasOwnProperty(key) && attrs[key] !== null) {
3490 					outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
3491 				}
3492 			}
3493 
3494 			// A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
3495 			if (typeof(html) != "undefined") {
3496 				return outHtml + '>' + html + '</' + name + '>';
3497 			}
3498 
3499 			return outHtml + ' />';
3500 		},
3501 
3502 		/**
3503 		 * Creates a document fragment out of the specified HTML string.
3504 		 *
3505 		 * @method createFragment
3506 		 * @param {String} html Html string to create fragment from.
3507 		 * @return {DocumentFragment} Document fragment node.
3508 		 */
3509 		createFragment: function(html) {
3510 			var frag, node, doc = this.doc, container;
3511 
3512 			container = doc.createElement("div");
3513 			frag = doc.createDocumentFragment();
3514 
3515 			if (html) {
3516 				container.innerHTML = html;
3517 			}
3518 
3519 			while ((node = container.firstChild)) {
3520 				frag.appendChild(node);
3521 			}
3522 
3523 			return frag;
3524 		},
3525 
3526 		/**
3527 		 * Removes/deletes the specified element(s) from the DOM.
3528 		 *
3529 		 * @method remove
3530 		 * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
3531 		 * @param {Boolean} keep_children Optional state to keep children or not. If set to true all children will be
3532 		 * placed at the location of the removed element.
3533 		 * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
3534 		 * were passed in.
3535 		 * @example
3536 		 * // Removes all paragraphs in the active editor
3537 		 * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
3538 		 *
3539 		 * // Removes an element by id in the document
3540 		 * tinymce.DOM.remove('mydiv');
3541 		 */
3542 		remove: function(node, keep_children) {
3543 			return this.run(node, function(node) {
3544 				var child, parent = node.parentNode;
3545 
3546 				if (!parent) {
3547 					return null;
3548 				}
3549 
3550 				if (keep_children) {
3551 					while ((child = node.firstChild)) {
3552 						// IE 8 will crash if you don't remove completely empty text nodes
3553 						if (!isIE || child.nodeType !== 3 || child.nodeValue) {
3554 							parent.insertBefore(child, node);
3555 						} else {
3556 							node.removeChild(child);
3557 						}
3558 					}
3559 				}
3560 
3561 				return parent.removeChild(node);
3562 			});
3563 		},
3564 
3565 		/**
3566 		 * Sets the CSS style value on a HTML element. The name can be a camelcase string
3567 		 * or the CSS style name like background-color.
3568 		 *
3569 		 * @method setStyle
3570 		 * @param {String/Element/Array} n HTML element/Element ID or Array of elements/ids to set CSS style value on.
3571 		 * @param {String} na Name of the style value to set.
3572 		 * @param {String} v Value to set on the style.
3573 		 * @example
3574 		 * // Sets a style value on all paragraphs in the currently active editor
3575 		 * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
3576 		 *
3577 		 * // Sets a style value to an element by id in the current document
3578 		 * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
3579 		 */
3580 		setStyle: function(elm, name, value) {
3581 			return this.run(elm, function(elm) {
3582 				var self = this, style, key;
3583 
3584 				if (name) {
3585 					if (typeof(name) === 'string') {
3586 						style = elm.style;
3587 
3588 						// Camelcase it, if needed
3589 						name = name.replace(/-(\D)/g, function(a, b) {
3590 							return b.toUpperCase();
3591 						});
3592 
3593 						// Default px suffix on these
3594 						if (typeof(value) === 'number' && !numericCssMap[name]) {
3595 							value += 'px';
3596 						}
3597 
3598 						// IE specific opacity
3599 						if (name === "opacity" && elm.runtimeStyle && typeof(elm.runtimeStyle.opacity) === "undefined") {
3600 							style.filter = value === '' ? '' : "alpha(opacity=" + (value * 100) + ")";
3601 						}
3602 
3603 						if (name == "float") {
3604 							// Old IE vs modern browsers
3605 							name = "cssFloat" in elm.style ? "cssFloat" : "styleFloat";
3606 						}
3607 
3608 						try {
3609 							style[name] = value;
3610 						} catch (ex) {
3611 							// Ignore IE errors
3612 						}
3613 
3614 						// Force update of the style data
3615 						if (self.settings.update_styles) {
3616 							elm.removeAttribute('data-mce-style');
3617 						}
3618 					} else {
3619 						for (key in name) {
3620 							self.setStyle(elm, key, name[key]);
3621 						}
3622 					}
3623 				}
3624 			});
3625 		},
3626 
3627 		/**
3628 		 * Returns the current style or runtime/computed value of an element.
3629 		 *
3630 		 * @method getStyle
3631 		 * @param {String/Element} elm HTML element or element id string to get style from.
3632 		 * @param {String} name Style name to return.
3633 		 * @param {Boolean} computed Computed style.
3634 		 * @return {String} Current style or computed style value of an element.
3635 		 */
3636 		getStyle: function(elm, name, computed) {
3637 			elm = this.get(elm);
3638 
3639 			if (!elm) {
3640 				return;
3641 			}
3642 
3643 			// W3C
3644 			if (this.doc.defaultView && computed) {
3645 				// Remove camelcase
3646 				name = name.replace(/[A-Z]/g, function(a){
3647 					return '-' + a;
3648 				});
3649 
3650 				try {
3651 					return this.doc.defaultView.getComputedStyle(elm, null).getPropertyValue(name);
3652 				} catch (ex) {
3653 					// Old safari might fail
3654 					return null;
3655 				}
3656 			}
3657 
3658 			// Camelcase it, if needed
3659 			name = name.replace(/-(\D)/g, function(a, b) {
3660 				return b.toUpperCase();
3661 			});
3662 
3663 			if (name == 'float') {
3664 				name = isIE ? 'styleFloat' : 'cssFloat';
3665 			}
3666 
3667 			// IE & Opera
3668 			if (elm.currentStyle && computed) {
3669 				return elm.currentStyle[name];
3670 			}
3671 
3672 			return elm.style ? elm.style[name] : undefined;
3673 		},
3674 
3675 		/**
3676 		 * Sets multiple styles on the specified element(s).
3677 		 *
3678 		 * @method setStyles
3679 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set styles on.
3680 		 * @param {Object} o Name/Value collection of style items to add to the element(s).
3681 		 * @example
3682 		 * // Sets styles on all paragraphs in the currently active editor
3683 		 * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
3684 		 *
3685 		 * // Sets styles to an element by id in the current document
3686 		 * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
3687 		 */
3688 		setStyles: function(elm, styles) {
3689 			this.setStyle(elm, styles);
3690 		},
3691 
3692 		css: function(elm, name, value) {
3693 			this.setStyle(elm, name, value);
3694 		},
3695 
3696 		/**
3697 		 * Removes all attributes from an element or elements.
3698 		 *
3699 		 * @method removeAllAttribs
3700 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
3701 		 */
3702 		removeAllAttribs: function(e) {
3703 			return this.run(e, function(e) {
3704 				var i, attrs = e.attributes;
3705 				for (i = attrs.length - 1; i >= 0; i--) {
3706 					e.removeAttributeNode(attrs.item(i));
3707 				}
3708 			});
3709 		},
3710 
3711 		/**
3712 		 * Sets the specified attribute of an element or elements.
3713 		 *
3714 		 * @method setAttrib
3715 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set attribute on.
3716 		 * @param {String} n Name of attribute to set.
3717 		 * @param {String} v Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove the attribute instead.
3718 		 * @example
3719 		 * // Sets class attribute on all paragraphs in the active editor
3720 		 * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
3721 		 *
3722 		 * // Sets class attribute on a specific element in the current page
3723 		 * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
3724 		 */
3725 		setAttrib: function(e, n, v) {
3726 			var self = this;
3727 
3728 			// What's the point
3729 			if (!e || !n) {
3730 				return;
3731 			}
3732 
3733 			return this.run(e, function(e) {
3734 				var s = self.settings;
3735 				var originalValue = e.getAttribute(n);
3736 				if (v !== null) {
3737 					switch (n) {
3738 						case "style":
3739 							if (!is(v, 'string')) {
3740 								each(v, function(v, n) {
3741 									self.setStyle(e, n, v);
3742 								});
3743 
3744 								return;
3745 							}
3746 
3747 							// No mce_style for elements with these since they might get resized by the user
3748 							if (s.keep_values) {
3749 								if (v) {
3750 									e.setAttribute('data-mce-style', v, 2);
3751 								} else {
3752 									e.removeAttribute('data-mce-style', 2);
3753 								}
3754 							}
3755 
3756 							e.style.cssText = v;
3757 							break;
3758 
3759 						case "class":
3760 							e.className = v || ''; // Fix IE null bug
3761 							break;
3762 
3763 						case "src":
3764 						case "href":
3765 							if (s.keep_values) {
3766 								if (s.url_converter) {
3767 									v = s.url_converter.call(s.url_converter_scope || self, v, n, e);
3768 								}
3769 
3770 								self.setAttrib(e, 'data-mce-' + n, v, 2);
3771 							}
3772 
3773 							break;
3774 
3775 						case "shape":
3776 							e.setAttribute('data-mce-style', v);
3777 							break;
3778 					}
3779 				}
3780 				if (is(v) && v !== null && v.length !== 0) {
3781 					e.setAttribute(n, '' + v, 2);
3782 				} else {
3783 					e.removeAttribute(n, 2);
3784 				}
3785 
3786 				// fire onChangeAttrib event for attributes that have changed
3787 				if (originalValue != v && s.onSetAttrib) {
3788 					s.onSetAttrib({attrElm: e, attrName: n, attrValue: v});
3789 				}
3790 			});
3791 		},
3792 
3793 		/**
3794 		 * Sets two or more specified attributes of an element or elements.
3795 		 *
3796 		 * @method setAttribs
3797 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
3798 		 * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
3799 		 * @example
3800 		 * // Sets class and title attributes on all paragraphs in the active editor
3801 		 * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
3802 		 *
3803 		 * // Sets class and title attributes on a specific element in the current page
3804 		 * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
3805 		 */
3806 		setAttribs: function(elm, attrs) {
3807 			var self = this;
3808 
3809 			return this.run(elm, function(elm) {
3810 				each(attrs, function(value, name) {
3811 					self.setAttrib(elm, name, value);
3812 				});
3813 			});
3814 		},
3815 
3816 		/**
3817 		 * Returns the specified attribute by name.
3818 		 *
3819 		 * @method getAttrib
3820 		 * @param {String/Element} elm Element string id or DOM element to get attribute from.
3821 		 * @param {String} name Name of attribute to get.
3822 		 * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
3823 		 * @return {String} Attribute value string, default value or null if the attribute wasn't found.
3824 		 */
3825 		getAttrib: function(elm, name, defaultVal) {
3826 			var value, self = this, undef;
3827 
3828 			elm = self.get(elm);
3829 
3830 			if (!elm || elm.nodeType !== 1) {
3831 				return defaultVal === undef ? false : defaultVal;
3832 			}
3833 
3834 			if (!is(defaultVal)) {
3835 				defaultVal = '';
3836 			}
3837 
3838 			// Try the mce variant for these
3839 			if (/^(src|href|style|coords|shape)$/.test(name)) {
3840 				value = elm.getAttribute("data-mce-" + name);
3841 
3842 				if (value) {
3843 					return value;
3844 				}
3845 			}
3846 
3847 			if (isIE && self.props[name]) {
3848 				value = elm[self.props[name]];
3849 				value = value && value.nodeValue ? value.nodeValue : value;
3850 			}
3851 
3852 			if (!value) {
3853 				value = elm.getAttribute(name, 2);
3854 			}
3855 
3856 			// Check boolean attribs
3857 			if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(name)) {
3858 				if (elm[self.props[name]] === true && value === '') {
3859 					return name;
3860 				}
3861 
3862 				return value ? name : '';
3863 			}
3864 
3865 			// Inner input elements will override attributes on form elements
3866 			if (elm.nodeName === "FORM" && elm.getAttributeNode(name)) {
3867 				return elm.getAttributeNode(name).nodeValue;
3868 			}
3869 
3870 			if (name === 'style') {
3871 				value = value || elm.style.cssText;
3872 
3873 				if (value) {
3874 					value = self.serializeStyle(self.parseStyle(value), elm.nodeName);
3875 
3876 					if (self.settings.keep_values) {
3877 						elm.setAttribute('data-mce-style', value);
3878 					}
3879 				}
3880 			}
3881 
3882 			// Remove Apple and WebKit stuff
3883 			if (isWebKit && name === "class" && value) {
3884 				value = value.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
3885 			}
3886 
3887 			// Handle IE issues
3888 			if (isIE) {
3889 				switch (name) {
3890 					case 'rowspan':
3891 					case 'colspan':
3892 						// IE returns 1 as default value
3893 						if (value === 1) {
3894 							value = '';
3895 						}
3896 
3897 						break;
3898 
3899 					case 'size':
3900 						// IE returns +0 as default value for size
3901 						if (value === '+0' || value === 20 || value === 0) {
3902 							value = '';
3903 						}
3904 
3905 						break;
3906 
3907 					case 'width':
3908 					case 'height':
3909 					case 'vspace':
3910 					case 'checked':
3911 					case 'disabled':
3912 					case 'readonly':
3913 						if (value === 0) {
3914 							value = '';
3915 						}
3916 
3917 						break;
3918 
3919 					case 'hspace':
3920 						// IE returns -1 as default value
3921 						if (value === -1) {
3922 							value = '';
3923 						}
3924 
3925 						break;
3926 
3927 					case 'maxlength':
3928 					case 'tabindex':
3929 						// IE returns default value
3930 						if (value === 32768 || value === 2147483647 || value === '32768') {
3931 							value = '';
3932 						}
3933 
3934 						break;
3935 
3936 					case 'multiple':
3937 					case 'compact':
3938 					case 'noshade':
3939 					case 'nowrap':
3940 						if (value === 65535) {
3941 							return name;
3942 						}
3943 
3944 						return defaultVal;
3945 
3946 					case 'shape':
3947 						value = value.toLowerCase();
3948 						break;
3949 
3950 					default:
3951 						// IE has odd anonymous function for event attributes
3952 						if (name.indexOf('on') === 0 && value) {
3953 							value = ('' + value).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');
3954 						}
3955 				}
3956 			}
3957 
3958 			return (value !== undef && value !== null && value !== '') ? '' + value : defaultVal;
3959 		},
3960 
3961 		/**
3962 		 * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
3963 		 *
3964 		 * @method getPos
3965 		 * @param {Element/String} elm HTML element or element id to get x, y position from.
3966 		 * @param {Element} rootElm Optional root element to stop calculations at.
3967 		 * @return {object} Absolute position of the specified element object with x, y fields.
3968 		 */
3969 		getPos: function(elm, rootElm) {
3970 			var self = this, x = 0, y = 0, offsetParent, doc = self.doc, pos;
3971 
3972 			elm = self.get(elm);
3973 			rootElm = rootElm || doc.body;
3974 
3975 			if (elm) {
3976 				// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
3977 				if (rootElm === doc.body && elm.getBoundingClientRect) {
3978 					pos = elm.getBoundingClientRect();
3979 					rootElm = self.boxModel ? doc.documentElement : doc.body;
3980 
3981 					// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
3982 					// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
3983 					x = pos.left + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - rootElm.clientLeft;
3984 					y = pos.top + (doc.documentElement.scrollTop || doc.body.scrollTop) - rootElm.clientTop;
3985 
3986 					return {x: x, y: y};
3987 				}
3988 
3989 				offsetParent = elm;
3990 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
3991 					x += offsetParent.offsetLeft || 0;
3992 					y += offsetParent.offsetTop || 0;
3993 					offsetParent = offsetParent.offsetParent;
3994 				}
3995 
3996 				offsetParent = elm.parentNode;
3997 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
3998 					x -= offsetParent.scrollLeft || 0;
3999 					y -= offsetParent.scrollTop || 0;
4000 					offsetParent = offsetParent.parentNode;
4001 				}
4002 			}
4003 
4004 			return {x: x, y: y};
4005 		},
4006 
4007 		/**
4008 		 * Parses the specified style value into an object collection. This parser will also
4009 		 * merge and remove any redundant items that browsers might have added. It will also convert non-hex
4010 		 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
4011 		 *
4012 		 * @method parseStyle
4013 		 * @param {String} cssText Style value to parse, for example: border:1px solid red;.
4014 		 * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
4015 		 */
4016 		parseStyle: function(cssText) {
4017 			return this.styles.parse(cssText);
4018 		},
4019 
4020 		/**
4021 		 * Serializes the specified style object into a string.
4022 		 *
4023 		 * @method serializeStyle
4024 		 * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
4025 		 * @param {String} name Optional element name.
4026 		 * @return {String} String representation of the style object, for example: border: 1px solid red.
4027 		 */
4028 		serializeStyle: function(styles, name) {
4029 			return this.styles.serialize(styles, name);
4030 		},
4031 
4032 		/**
4033 		 * Adds a style element at the top of the document with the specified cssText content.
4034 		 *
4035 		 * @method addStyle
4036 		 * @param {String} cssText CSS Text style to add to top of head of document.
4037 		 */
4038 		addStyle: function(cssText) {
4039 			var self = this, doc = self.doc, head, styleElm;
4040 
4041 			// Prevent inline from loading the same styles twice
4042 			if (self !== DOMUtils.DOM && doc === document) {
4043 				var addedStyles = DOMUtils.DOM.addedStyles;
4044 
4045 				addedStyles = addedStyles || [];
4046 				if (addedStyles[cssText]) {
4047 					return;
4048 				}
4049 
4050 				addedStyles[cssText] = true;
4051 				DOMUtils.DOM.addedStyles = addedStyles;
4052 			}
4053 
4054 			// Create style element if needed
4055 			styleElm = doc.getElementById('mceDefaultStyles');
4056 			if (!styleElm) {
4057 				styleElm = doc.createElement('style');
4058 				styleElm.id = 'mceDefaultStyles';
4059 				styleElm.type = 'text/css';
4060 
4061 				head = doc.getElementsByTagName('head')[0];
4062 				if (head.firstChild) {
4063 					head.insertBefore(styleElm, head.firstChild);
4064 				} else {
4065 					head.appendChild(styleElm);
4066 				}
4067 			}
4068 
4069 			// Append style data to old or new style element
4070 			if (styleElm.styleSheet) {
4071 				styleElm.styleSheet.cssText += cssText;
4072 			} else {
4073 				styleElm.appendChild(doc.createTextNode(cssText));
4074 			}
4075 		},
4076 
4077 		/**
4078 		 * Imports/loads the specified CSS file into the document bound to the class.
4079 		 *
4080 		 * @method loadCSS
4081 		 * @param {String} u URL to CSS file to load.
4082 		 * @example
4083 		 * // Loads a CSS file dynamically into the current document
4084 		 * tinymce.DOM.loadCSS('somepath/some.css');
4085 		 *
4086 		 * // Loads a CSS file into the currently active editor instance
4087 		 * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
4088 		 *
4089 		 * // Loads a CSS file into an editor instance by id
4090 		 * tinymce.get('someid').dom.loadCSS('somepath/some.css');
4091 		 *
4092 		 * // Loads multiple CSS files into the current document
4093 		 * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
4094 		 */
4095 		loadCSS: function(url) {
4096 			var self = this, doc = self.doc, head;
4097 
4098 			// Prevent inline from loading the same CSS file twice
4099 			if (self !== DOMUtils.DOM && doc === document) {
4100 				DOMUtils.DOM.loadCSS(url);
4101 				return;
4102 			}
4103 
4104 			if (!url) {
4105 				url = '';
4106 			}
4107 
4108 			head = doc.getElementsByTagName('head')[0];
4109 
4110 			each(url.split(','), function(url) {
4111 				var link;
4112 
4113 				if (self.files[url]) {
4114 					return;
4115 				}
4116 
4117 				self.files[url] = true;
4118 				link = self.create('link', {rel: 'stylesheet', href: url});
4119 
4120 				// IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
4121 				// This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
4122 				// It's ugly but it seems to work fine.
4123 				if (isIE && doc.documentMode && doc.recalc) {
4124 					link.onload = function() {
4125 						if (doc.recalc) {
4126 							doc.recalc();
4127 						}
4128 
4129 						link.onload = null;
4130 					};
4131 				}
4132 
4133 				head.appendChild(link);
4134 			});
4135 		},
4136 
4137 		/**
4138 		 * Adds a class to the specified element or elements.
4139 		 *
4140 		 * @method addClass
4141 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
4142 		 * @param {String} cls Class name to add to each element.
4143 		 * @return {String/Array} String with new class value or array with new class values for all elements.
4144 		 * @example
4145 		 * // Adds a class to all paragraphs in the active editor
4146 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
4147 		 *
4148 		 * // Adds a class to a specific element in the current page
4149 		 * tinymce.DOM.addClass('mydiv', 'myclass');
4150 		 */
4151 		addClass: function(elm, cls) {
4152 			return this.run(elm, function(elm) {
4153 				var clsVal;
4154 
4155 				if (!cls) {
4156 					return 0;
4157 				}
4158 
4159 				if (this.hasClass(elm, cls)) {
4160 					return elm.className;
4161 				}
4162 
4163 				clsVal = this.removeClass(elm, cls);
4164 				elm.className = clsVal = (clsVal !== '' ? (clsVal + ' ') : '') + cls;
4165 
4166 				return clsVal;
4167 			});
4168 		},
4169 
4170 		/**
4171 		 * Removes a class from the specified element or elements.
4172 		 *
4173 		 * @method removeClass
4174 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
4175 		 * @param {String} cls Class name to remove from each element.
4176 		 * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
4177 		 * were passed in.
4178 		 * @example
4179 		 * // Removes a class from all paragraphs in the active editor
4180 		 * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
4181 		 *
4182 		 * // Removes a class from a specific element in the current page
4183 		 * tinymce.DOM.removeClass('mydiv', 'myclass');
4184 		 */
4185 		removeClass: function(elm, cls) {
4186 			var self = this, re;
4187 
4188 			return self.run(elm, function(elm) {
4189 				var val;
4190 
4191 				if (self.hasClass(elm, cls)) {
4192 					if (!re) {
4193 						re = new RegExp("(^|\\s+)" + cls + "(\\s+|$)", "g");
4194 					}
4195 
4196 					val = elm.className.replace(re, ' ');
4197 					val = trim(val != ' ' ? val : '');
4198 
4199 					elm.className = val;
4200 
4201 					// Empty class attr
4202 					if (!val) {
4203 						elm.removeAttribute('class');
4204 						elm.removeAttribute('className');
4205 					}
4206 
4207 					return val;
4208 				}
4209 
4210 				return elm.className;
4211 			});
4212 		},
4213 
4214 		/**
4215 		 * Returns true if the specified element has the specified class.
4216 		 *
4217 		 * @method hasClass
4218 		 * @param {String/Element} n HTML element or element id string to check CSS class on.
4219 		 * @param {String} c CSS class to check for.
4220 		 * @return {Boolean} true/false if the specified element has the specified class.
4221 		 */
4222 		hasClass: function(elm, cls) {
4223 			elm = this.get(elm);
4224 
4225 			if (!elm || !cls) {
4226 				return false;
4227 			}
4228 
4229 			return (' ' + elm.className + ' ').indexOf(' ' + cls + ' ') !== -1;
4230 		},
4231 
4232 		/**
4233 		 * Toggles the specified class on/off.
4234 		 *
4235 		 * @method toggleClass
4236 		 * @param {Element} elm Element to toggle class on.
4237 		 * @param {[type]} cls Class to toggle on/off.
4238 		 * @param {[type]} state Optional state to set.
4239 		 */
4240 		toggleClass: function(elm, cls, state) {
4241 			state = state === undefined ? !this.hasClass(elm, cls) : state;
4242 
4243 			if (this.hasClass(elm, cls) !== state) {
4244 				if (state) {
4245 					this.addClass(elm, cls);
4246 				} else {
4247 					this.removeClass(elm, cls);
4248 				}
4249 			}
4250 		},
4251 
4252 		/**
4253 		 * Shows the specified element(s) by ID by setting the "display" style.
4254 		 *
4255 		 * @method show
4256 		 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
4257 		 */
4258 		show: function(elm) {
4259 			return this.setStyle(elm, 'display', 'block');
4260 		},
4261 
4262 		/**
4263 		 * Hides the specified element(s) by ID by setting the "display" style.
4264 		 *
4265 		 * @method hide
4266 		 * @param {String/Element/Array} e ID of DOM element or DOM element or array with elements or IDs to hide.
4267 		 * @example
4268 		 * // Hides an element by id in the document
4269 		 * tinymce.DOM.hide('myid');
4270 		 */
4271 		hide: function(elm) {
4272 			return this.setStyle(elm, 'display', 'none');
4273 		},
4274 
4275 		/**
4276 		 * Returns true/false if the element is hidden or not by checking the "display" style.
4277 		 *
4278 		 * @method isHidden
4279 		 * @param {String/Element} e Id or element to check display state on.
4280 		 * @return {Boolean} true/false if the element is hidden or not.
4281 		 */
4282 		isHidden: function(elm) {
4283 			elm = this.get(elm);
4284 
4285 			return !elm || elm.style.display == 'none' || this.getStyle(elm, 'display') == 'none';
4286 		},
4287 
4288 		/**
4289 		 * Returns a unique id. This can be useful when generating elements on the fly.
4290 		 * This method will not check if the element already exists.
4291 		 *
4292 		 * @method uniqueId
4293 		 * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
4294 		 * @return {String} Unique id.
4295 		 */
4296 		uniqueId: function(prefix) {
4297 			return (!prefix ? 'mce_' : prefix) + (this.counter++);
4298 		},
4299 
4300 		/**
4301 		 * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
4302 		 * URLs will get converted, hex color values fixed etc. Check processHTML for details.
4303 		 *
4304 		 * @method setHTML
4305 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set HTML inside of.
4306 		 * @param {String} h HTML content to set as inner HTML of the element.
4307 		 * @example
4308 		 * // Sets the inner HTML of all paragraphs in the active editor
4309 		 * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
4310 		 *
4311 		 * // Sets the inner HTML of an element by id in the document
4312 		 * tinymce.DOM.setHTML('mydiv', 'some inner html');
4313 		 */
4314 		setHTML: function(element, html) {
4315 			var self = this;
4316 
4317 			return self.run(element, function(element) {
4318 				if (isIE) {
4319 					// Remove all child nodes, IE keeps empty text nodes in DOM
4320 					while (element.firstChild) {
4321 						element.removeChild(element.firstChild);
4322 					}
4323 
4324 					try {
4325 						// IE will remove comments from the beginning
4326 						// unless you padd the contents with something
4327 						element.innerHTML = '<br />' + html;
4328 						element.removeChild(element.firstChild);
4329 					} catch (ex) {
4330 						// IE sometimes produces an unknown runtime error on innerHTML if it's a block element
4331 						// within a block element for example a div inside a p
4332 						// This seems to fix this problem
4333 
4334 						// Create new div with HTML contents and a BR in front to keep comments
4335 						var newElement = self.create('div');
4336 						newElement.innerHTML = '<br />' + html;
4337 
4338 						// Add all children from div to target
4339 						each(grep(newElement.childNodes), function(node, i) {
4340 							// Skip br element
4341 							if (i && element.canHaveHTML) {
4342 								element.appendChild(node);
4343 							}
4344 						});
4345 					}
4346 				} else {
4347 					element.innerHTML = html;
4348 				}
4349 
4350 				return html;
4351 			});
4352 		},
4353 
4354 		/**
4355 		 * Returns the outer HTML of an element.
4356 		 *
4357 		 * @method getOuterHTML
4358 		 * @param {String/Element} elm Element ID or element object to get outer HTML from.
4359 		 * @return {String} Outer HTML string.
4360 		 * @example
4361 		 * tinymce.DOM.getOuterHTML(editorElement);
4362 		 * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
4363 		 */
4364 		getOuterHTML: function(elm) {
4365 			var doc, self = this;
4366 
4367 			elm = self.get(elm);
4368 
4369 			if (!elm) {
4370 				return null;
4371 			}
4372 
4373 			if (elm.nodeType === 1 && self.hasOuterHTML) {
4374 				return elm.outerHTML;
4375 			}
4376 
4377 			doc = (elm.ownerDocument || self.doc).createElement("body");
4378 			doc.appendChild(elm.cloneNode(true));
4379 
4380 			return doc.innerHTML;
4381 		},
4382 
4383 		/**
4384 		 * Sets the specified outer HTML on an element or elements.
4385 		 *
4386 		 * @method setOuterHTML
4387 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
4388 		 * @param {Object} html HTML code to set as outer value for the element.
4389 		 * @param {Document} doc Optional document scope to use in this process - defaults to the document of the DOM class.
4390 		 * @example
4391 		 * // Sets the outer HTML of all paragraphs in the active editor
4392 		 * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
4393 		 *
4394 		 * // Sets the outer HTML of an element by id in the document
4395 		 * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
4396 		 */
4397 		setOuterHTML: function(elm, html, doc) {
4398 			var self = this;
4399 
4400 			return self.run(elm, function(elm) {
4401 				function set() {
4402 					var node, tempElm;
4403 
4404 					tempElm = doc.createElement("body");
4405 					tempElm.innerHTML = html;
4406 
4407 					node = tempElm.lastChild;
4408 					while (node) {
4409 						self.insertAfter(node.cloneNode(true), elm);
4410 						node = node.previousSibling;
4411 					}
4412 
4413 					self.remove(elm);
4414 				}
4415 
4416 				// Only set HTML on elements
4417 				if (elm.nodeType == 1) {
4418 					doc = doc || elm.ownerDocument || self.doc;
4419 
4420 					if (isIE) {
4421 						try {
4422 							// Try outerHTML for IE it sometimes produces an unknown runtime error
4423 							if (elm.nodeType == 1 && self.hasOuterHTML) {
4424 								elm.outerHTML = html;
4425 							} else {
4426 								set();
4427 							}
4428 						} catch (ex) {
4429 							// Fix for unknown runtime error
4430 							set();
4431 						}
4432 					} else {
4433 						set();
4434 					}
4435 				}
4436 			});
4437 		},
4438 
4439 		/**
4440 		 * Entity decodes a string. This method decodes any HTML entities, such as å.
4441 		 *
4442 		 * @method decode
4443 		 * @param {String} s String to decode entities on.
4444 		 * @return {String} Entity decoded string.
4445 		 */
4446 		decode: Entities.decode,
4447 
4448 		/**
4449 		 * Entity encodes a string. This method encodes the most common entities, such as <>"&.
4450 		 *
4451 		 * @method encode
4452 		 * @param {String} text String to encode with entities.
4453 		 * @return {String} Entity encoded string.
4454 		 */
4455 		encode: Entities.encodeAllRaw,
4456 
4457 		/**
4458 		 * Inserts an element after the reference element.
4459 		 *
4460 		 * @method insertAfter
4461 		 * @param {Element} node Element to insert after the reference.
4462 		 * @param {Element/String/Array} reference_node Reference element, element id or array of elements to insert after.
4463 		 * @return {Element/Array} Element that got added or an array with elements.
4464 		 */
4465 		insertAfter: function(node, reference_node) {
4466 			reference_node = this.get(reference_node);
4467 
4468 			return this.run(node, function(node) {
4469 				var parent, nextSibling;
4470 
4471 				parent = reference_node.parentNode;
4472 				nextSibling = reference_node.nextSibling;
4473 
4474 				if (nextSibling) {
4475 					parent.insertBefore(node, nextSibling);
4476 				} else {
4477 					parent.appendChild(node);
4478 				}
4479 
4480 				return node;
4481 			});
4482 		},
4483 
4484 		/**
4485 		 * Replaces the specified element or elements with the new element specified. The new element will
4486 		 * be cloned if multiple input elements are passed in.
4487 		 *
4488 		 * @method replace
4489 		 * @param {Element} newElm New element to replace old ones with.
4490 		 * @param {Element/String/Array} oldELm Element DOM node, element id or array of elements or ids to replace.
4491 		 * @param {Boolean} k Optional keep children state, if set to true child nodes from the old object will be added to new ones.
4492 		 */
4493 		replace: function(newElm, oldElm, keepChildren) {
4494 			var self = this;
4495 
4496 			return self.run(oldElm, function(oldElm) {
4497 				if (is(oldElm, 'array')) {
4498 					newElm = newElm.cloneNode(true);
4499 				}
4500 
4501 				if (keepChildren) {
4502 					each(grep(oldElm.childNodes), function(node) {
4503 						newElm.appendChild(node);
4504 					});
4505 				}
4506 
4507 				return oldElm.parentNode.replaceChild(newElm, oldElm);
4508 			});
4509 		},
4510 
4511 		/**
4512 		 * Renames the specified element and keeps its attributes and children.
4513 		 *
4514 		 * @method rename
4515 		 * @param {Element} elm Element to rename.
4516 		 * @param {String} name Name of the new element.
4517 		 * @return {Element} New element or the old element if it needed renaming.
4518 		 */
4519 		rename: function(elm, name) {
4520 			var self = this, newElm;
4521 
4522 			if (elm.nodeName != name.toUpperCase()) {
4523 				// Rename block element
4524 				newElm = self.create(name);
4525 
4526 				// Copy attribs to new block
4527 				each(self.getAttribs(elm), function(attr_node) {
4528 					self.setAttrib(newElm, attr_node.nodeName, self.getAttrib(elm, attr_node.nodeName));
4529 				});
4530 
4531 				// Replace block
4532 				self.replace(newElm, elm, 1);
4533 			}
4534 
4535 			return newElm || elm;
4536 		},
4537 
4538 		/**
4539 		 * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
4540 		 *
4541 		 * @method findCommonAncestor
4542 		 * @param {Element} a Element to find common ancestor of.
4543 		 * @param {Element} b Element to find common ancestor of.
4544 		 * @return {Element} Common ancestor element of the two input elements.
4545 		 */
4546 		findCommonAncestor: function(a, b) {
4547 			var ps = a, pe;
4548 
4549 			while (ps) {
4550 				pe = b;
4551 
4552 				while (pe && ps != pe) {
4553 					pe = pe.parentNode;
4554 				}
4555 
4556 				if (ps == pe) {
4557 					break;
4558 				}
4559 
4560 				ps = ps.parentNode;
4561 			}
4562 
4563 			if (!ps && a.ownerDocument) {
4564 				return a.ownerDocument.documentElement;
4565 			}
4566 
4567 			return ps;
4568 		},
4569 
4570 		/**
4571 		 * Parses the specified RGB color value and returns a hex version of that color.
4572 		 *
4573 		 * @method toHex
4574 		 * @param {String} rgbVal RGB string value like rgb(1,2,3)
4575 		 * @return {String} Hex version of that RGB value like #FF00FF.
4576 		 */
4577 		toHex: function(rgbVal) {
4578 			return this.styles.toHex(Tools.trim(rgbVal));
4579 		},
4580 
4581 		/**
4582 		 * Executes the specified function on the element by id or dom element node or array of elements/id.
4583 		 *
4584 		 * @method run
4585 		 * @param {String/Element/Array} Element ID or DOM element object or array with ids or elements.
4586 		 * @param {function} f Function to execute for each item.
4587 		 * @param {Object} s Optional scope to execute the function in.
4588 		 * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
4589 		 */
4590 		run: function(elm, func, scope) {
4591 			var self = this, result;
4592 
4593 			if (typeof(elm) === 'string') {
4594 				elm = self.get(elm);
4595 			}
4596 
4597 			if (!elm) {
4598 				return false;
4599 			}
4600 
4601 			scope = scope || this;
4602 			if (!elm.nodeType && (elm.length || elm.length === 0)) {
4603 				result = [];
4604 
4605 				each(elm, function(elm, i) {
4606 					if (elm) {
4607 						if (typeof(elm) == 'string') {
4608 							elm = self.get(elm);
4609 						}
4610 
4611 						result.push(func.call(scope, elm, i));
4612 					}
4613 				});
4614 
4615 				return result;
4616 			}
4617 
4618 			return func.call(scope, elm);
4619 		},
4620 
4621 		/**
4622 		 * Returns a NodeList with attributes for the element.
4623 		 *
4624 		 * @method getAttribs
4625 		 * @param {HTMLElement/string} elm Element node or string id to get attributes from.
4626 		 * @return {NodeList} NodeList with attributes.
4627 		 */
4628 		getAttribs: function(elm) {
4629 			var attrs;
4630 
4631 			elm = this.get(elm);
4632 
4633 			if (!elm) {
4634 				return [];
4635 			}
4636 
4637 			if (isIE) {
4638 				attrs = [];
4639 
4640 				// Object will throw exception in IE
4641 				if (elm.nodeName == 'OBJECT') {
4642 					return elm.attributes;
4643 				}
4644 
4645 				// IE doesn't keep the selected attribute if you clone option elements
4646 				if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
4647 					attrs.push({specified: 1, nodeName: 'selected'});
4648 				}
4649 
4650 				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
4651 				var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
4652 				elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
4653 					attrs.push({specified: 1, nodeName: a});
4654 				});
4655 
4656 				return attrs;
4657 			}
4658 
4659 			return elm.attributes;
4660 		},
4661 
4662 		/**
4663 		 * Returns true/false if the specified node is to be considered empty or not.
4664 		 *
4665 		 * @example
4666 		 * tinymce.DOM.isEmpty(node, {img: true});
4667 		 * @method isEmpty
4668 		 * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
4669 		 * @return {Boolean} true/false if the node is empty or not.
4670 		 */
4671 		isEmpty: function(node, elements) {
4672 			var self = this, i, attributes, type, walker, name, brCount = 0;
4673 
4674 			node = node.firstChild;
4675 			if (node) {
4676 				walker = new TreeWalker(node, node.parentNode);
4677 				elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;
4678 
4679 				do {
4680 					type = node.nodeType;
4681 
4682 					if (type === 1) {
4683 						// Ignore bogus elements
4684 						if (node.getAttribute('data-mce-bogus')) {
4685 							continue;
4686 						}
4687 
4688 						// Keep empty elements like <img />
4689 						name = node.nodeName.toLowerCase();
4690 						if (elements && elements[name]) {
4691 							// Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
4692 							if (name === 'br') {
4693 								brCount++;
4694 								continue;
4695 							}
4696 
4697 							return false;
4698 						}
4699 
4700 						// Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
4701 						attributes = self.getAttribs(node);
4702 						i = attributes.length;
4703 						while (i--) {
4704 							name = attributes[i].nodeName;
4705 							if (name === "name" || name === 'data-mce-bookmark') {
4706 								return false;
4707 							}
4708 						}
4709 					}
4710 
4711 					// Keep comment nodes
4712 					if (type == 8) {
4713 						return false;
4714 					}
4715 
4716 					// Keep non whitespace text nodes
4717 					if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
4718 						return false;
4719 					}
4720 				} while ((node = walker.next()));
4721 			}
4722 
4723 			return brCount <= 1;
4724 		},
4725 
4726 		/**
4727 		 * Creates a new DOM Range object. This will use the native DOM Range API if it's
4728 		 * available. If it's not, it will fall back to the custom TinyMCE implementation.
4729 		 *
4730 		 * @method createRng
4731 		 * @return {DOMRange} DOM Range object.
4732 		 * @example
4733 		 * var rng = tinymce.DOM.createRng();
4734 		 * alert(rng.startContainer + "," + rng.startOffset);
4735 		 */
4736 		createRng: function() {
4737 			var doc = this.doc;
4738 
4739 			return doc.createRange ? doc.createRange() : new Range(this);
4740 		},
4741 
4742 		/**
4743 		 * Returns the index of the specified node within its parent.
4744 		 *
4745 		 * @method nodeIndex
4746 		 * @param {Node} node Node to look for.
4747 		 * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
4748 		 * @return {Number} Index of the specified node.
4749 		 */
4750 		nodeIndex: function(node, normalized) {
4751 			var idx = 0, lastNodeType, nodeType;
4752 
4753 			if (node) {
4754 				for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
4755 					nodeType = node.nodeType;
4756 
4757 					// Normalize text nodes
4758 					if (normalized && nodeType == 3) {
4759 						if (nodeType == lastNodeType || !node.nodeValue.length) {
4760 							continue;
4761 						}
4762 					}
4763 					idx++;
4764 					lastNodeType = nodeType;
4765 				}
4766 			}
4767 
4768 			return idx;
4769 		},
4770 
4771 		/**
4772 		 * Splits an element into two new elements and places the specified split
4773 		 * element or elements between the new ones. For example splitting the paragraph at the bold element in
4774 		 * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>.
4775 		 *
4776 		 * @method split
4777 		 * @param {Element} parentElm Parent element to split.
4778 		 * @param {Element} splitElm Element to split at.
4779 		 * @param {Element} replacementElm Optional replacement element to replace the split element with.
4780 		 * @return {Element} Returns the split element or the replacement element if that is specified.
4781 		 */
4782 		split: function(parentElm, splitElm, replacementElm) {
4783 			var self = this, r = self.createRng(), bef, aft, pa;
4784 
4785 			// W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
4786 			// but we don't want that in our code since it serves no purpose for the end user
4787 			// For example splitting this html at the bold element:
4788 			//   <p>text 1<span><b>CHOP</b></span>text 2</p>
4789 			// would produce:
4790 			//   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
4791 			// this function will then trim off empty edges and produce:
4792 			//   <p>text 1</p><b>CHOP</b><p>text 2</p>
4793 			function trimNode(node) {
4794 				var i, children = node.childNodes, type = node.nodeType;
4795 
4796 				function surroundedBySpans(node) {
4797 					var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
4798 					var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
4799 					return previousIsSpan && nextIsSpan;
4800 				}
4801 
4802 				if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
4803 					return;
4804 				}
4805 
4806 				for (i = children.length - 1; i >= 0; i--) {
4807 					trimNode(children[i]);
4808 				}
4809 
4810 				if (type != 9) {
4811 					// Keep non whitespace text nodes
4812 					if (type == 3 && node.nodeValue.length > 0) {
4813 						// If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
4814 						// Also keep text nodes with only spaces if surrounded by spans.
4815 						// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
4816 						var trimmedLength = trim(node.nodeValue).length;
4817 						if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
4818 							return;
4819 						}
4820 					} else if (type == 1) {
4821 						// If the only child is a bookmark then move it up
4822 						children = node.childNodes;
4823 
4824 						// TODO fix this complex if
4825 						if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
4826 							children[0].getAttribute('data-mce-type') == 'bookmark') {
4827 							node.parentNode.insertBefore(children[0], node);
4828 						}
4829 
4830 						// Keep non empty elements or img, hr etc
4831 						if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
4832 							return;
4833 						}
4834 					}
4835 
4836 					self.remove(node);
4837 				}
4838 
4839 				return node;
4840 			}
4841 
4842 			if (parentElm && splitElm) {
4843 				// Get before chunk
4844 				r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
4845 				r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
4846 				bef = r.extractContents();
4847 
4848 				// Get after chunk
4849 				r = self.createRng();
4850 				r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
4851 				r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
4852 				aft = r.extractContents();
4853 
4854 				// Insert before chunk
4855 				pa = parentElm.parentNode;
4856 				pa.insertBefore(trimNode(bef), parentElm);
4857 
4858 				// Insert middle chunk
4859 				if (replacementElm) {
4860 					pa.replaceChild(replacementElm, splitElm);
4861 				} else {
4862 					pa.insertBefore(splitElm, parentElm);
4863 				}
4864 
4865 				// Insert after chunk
4866 				pa.insertBefore(trimNode(aft), parentElm);
4867 				self.remove(parentElm);
4868 
4869 				return replacementElm || splitElm;
4870 			}
4871 		},
4872 
4873 		/**
4874 		 * Adds an event handler to the specified object.
4875 		 *
4876 		 * @method bind
4877 		 * @param {Element/Document/Window/Array} target Target element to bind events to.
4878 		 * handler to or an array of elements/ids/documents.
4879 		 * @param {String} name Name of event handler to add, for example: click.
4880 		 * @param {function} func Function to execute when the event occurs.
4881 		 * @param {Object} scope Optional scope to execute the function in.
4882 		 * @return {function} Function callback handler the same as the one passed in.
4883 		 */
4884 		bind: function(target, name, func, scope) {
4885 			var self = this;
4886 
4887 			if (Tools.isArray(target)) {
4888 				var i = target.length;
4889 
4890 				while (i--) {
4891 					target[i] = self.bind(target[i], name, func, scope);
4892 				}
4893 
4894 				return target;
4895 			}
4896 
4897 			// Collect all window/document events bound by editor instance
4898 			if (self.settings.collect && (target === self.doc || target === self.win)) {
4899 				self.boundEvents.push([target, name, func, scope]);
4900 			}
4901 
4902 			return self.events.bind(target, name, func, scope || self);
4903 		},
4904 
4905 		/**
4906 		 * Removes the specified event handler by name and function from an element or collection of elements.
4907 		 *
4908 		 * @method unbind
4909 		 * @param {Element/Document/Window/Array} target Target element to unbind events on.
4910 		 * @param {String} name Event handler name, for example: "click"
4911 		 * @param {function} func Function to remove.
4912 		 * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
4913 		 * were passed in.
4914 		 */
4915 		unbind: function(target, name, func) {
4916 			var self = this, i;
4917 
4918 			if (Tools.isArray(target)) {
4919 				i = target.length;
4920 
4921 				while (i--) {
4922 					target[i] = self.unbind(target[i], name, func);
4923 				}
4924 
4925 				return target;
4926 			}
4927 
4928 			// Remove any bound events matching the input
4929 			if (self.boundEvents && (target === self.doc || target === self.win)) {
4930 				i = self.boundEvents.length;
4931 
4932 				while (i--) {
4933 					var item = self.boundEvents[i];
4934 
4935 					if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
4936 						this.events.unbind(item[0], item[1], item[2]);
4937 					}
4938 				}
4939 			}
4940 
4941 			return this.events.unbind(target, name, func);
4942 		},
4943 
4944 		/**
4945 		 * Fires the specified event name with object on target.
4946 		 *
4947 		 * @method fire
4948 		 * @param {Node/Document/Window} target Target element or object to fire event on.
4949 		 * @param {String} name Name of the event to fire.
4950 		 * @param {Object} evt Event object to send.
4951 		 * @return {Event} Event object.
4952 		 */
4953 		fire: function(target, name, evt) {
4954 			return this.events.fire(target, name, evt);
4955 		},
4956 
4957 		// Returns the content editable state of a node
4958 		getContentEditable: function(node) {
4959 			var contentEditable;
4960 
4961 			// Check type
4962 			if (!node || node.nodeType != 1) {
4963 				return null;
4964 			}
4965 
4966 			// Check for fake content editable
4967 			contentEditable = node.getAttribute("data-mce-contenteditable");
4968 			if (contentEditable && contentEditable !== "inherit") {
4969 				return contentEditable;
4970 			}
4971 
4972 			// Check for real content editable
4973 			return node.contentEditable !== "inherit" ? node.contentEditable : null;
4974 		},
4975 
4976 		getContentEditableParent: function(node) {
4977 			var root = this.getRoot(), state = null;
4978 
4979 			for (; node && node !== root; node = node.parentNode) {
4980 				state = this.getContentEditable(node);
4981 
4982 				if (state !== null) {
4983 					break;
4984 				}
4985 			}
4986 
4987 			return state;
4988 		},
4989 
4990 		/**
4991 		 * Destroys all internal references to the DOM to solve IE leak issues.
4992 		 *
4993 		 * @method destroy
4994 		 */
4995 		destroy: function() {
4996 			var self = this;
4997 
4998 			// Unbind all events bound to window/document by editor instance
4999 			if (self.boundEvents) {
5000 				var i = self.boundEvents.length;
5001 
5002 				while (i--) {
5003 					var item = self.boundEvents[i];
5004 					this.events.unbind(item[0], item[1], item[2]);
5005 				}
5006 
5007 				self.boundEvents = null;
5008 			}
5009 
5010 			// Restore sizzle document to window.document
5011 			// Since the current document might be removed producing "Permission denied" on IE see #6325
5012 			if (Sizzle.setDocument) {
5013 				Sizzle.setDocument();
5014 			}
5015 
5016 			self.win = self.doc = self.root = self.events = self.frag = null;
5017 		},
5018 
5019 		isChildOf: function(node, parent) {
5020 			if (parent.contains) {
5021 				return parent.contains(node);
5022 			}
5023 
5024 			while (node) {
5025 				if (parent === node) {
5026 					return true;
5027 				}
5028 
5029 				node = node.parentNode;
5030 			}
5031 
5032 			return false;
5033 		},
5034 
5035 		// #ifdef debug
5036 
5037 		dumpRng: function(r) {
5038 			return (
5039 				'startContainer: ' + r.startContainer.nodeName +
5040 				', startOffset: ' + r.startOffset +
5041 				', endContainer: ' + r.endContainer.nodeName +
5042 				', endOffset: ' + r.endOffset
5043 			);
5044 		},
5045 
5046 		// #endif
5047 
5048 		_findSib: function(node, selector, name) {
5049 			var self = this, func = selector;
5050 
5051 			if (node) {
5052 				// If expression make a function of it using is
5053 				if (typeof(func) == 'string') {
5054 					func = function(node) {
5055 						return self.is(node, selector);
5056 					};
5057 				}
5058 
5059 				// Loop all siblings
5060 				for (node = node[name]; node; node = node[name]) {
5061 					if (func(node)) {
5062 						return node;
5063 					}
5064 				}
5065 			}
5066 
5067 			return null;
5068 		}
5069 	};
5070 
5071 	/**
5072 	 * Instance of DOMUtils for the current document.
5073 	 *
5074 	 * @static
5075 	 * @property DOM
5076 	 * @type tinymce.dom.DOMUtils
5077 	 * @example
5078 	 * // Example of how to add a class to some element by id
5079 	 * tinymce.DOM.addClass('someid', 'someclass');
5080 	 */
5081 	DOMUtils.DOM = new DOMUtils(document);
5082 
5083 	return DOMUtils;
5084 });
5085 
5086 // Included from: js/tinymce/classes/dom/ScriptLoader.js
5087 
5088 /**
5089  * ScriptLoader.js
5090  *
5091  * Copyright, Moxiecode Systems AB
5092  * Released under LGPL License.
5093  *
5094  * License: http://www.tinymce.com/license
5095  * Contributing: http://www.tinymce.com/contributing
5096  */
5097 
5098 /*globals console*/
5099 
5100 /**
5101  * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
5102  * when various items gets loaded. This class is useful to load external JavaScript files.
5103  *
5104  * @class tinymce.dom.ScriptLoader
5105  * @example
5106  * // Load a script from a specific URL using the global script loader
5107  * tinymce.ScriptLoader.load('somescript.js');
5108  *
5109  * // Load a script using a unique instance of the script loader
5110  * var scriptLoader = new tinymce.dom.ScriptLoader();
5111  *
5112  * scriptLoader.load('somescript.js');
5113  *
5114  * // Load multiple scripts
5115  * var scriptLoader = new tinymce.dom.ScriptLoader();
5116  *
5117  * scriptLoader.add('somescript1.js');
5118  * scriptLoader.add('somescript2.js');
5119  * scriptLoader.add('somescript3.js');
5120  *
5121  * scriptLoader.loadQueue(function() {
5122  *    alert('All scripts are now loaded.');
5123  * });
5124  */
5125 define("tinymce/dom/ScriptLoader", [
5126 	"tinymce/dom/DOMUtils",
5127 	"tinymce/util/Tools"
5128 ], function(DOMUtils, Tools) {
5129 	var DOM = DOMUtils.DOM;
5130 	var each = Tools.each, grep = Tools.grep;
5131 
5132 	function ScriptLoader() {
5133 		var QUEUED = 0,
5134 			LOADING = 1,
5135 			LOADED = 2,
5136 			states = {},
5137 			queue = [],
5138 			scriptLoadedCallbacks = {},
5139 			queueLoadedCallbacks = [],
5140 			loading = 0,
5141 			undef;
5142 
5143 		/**
5144 		 * Loads a specific script directly without adding it to the load queue.
5145 		 *
5146 		 * @method load
5147 		 * @param {String} url Absolute URL to script to add.
5148 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
5149 		 * @param {Object} scope Optional scope to execute callback in.
5150 		 */
5151 		function loadScript(url, callback) {
5152 			var dom = DOM, elm, id;
5153 
5154 			// Execute callback when script is loaded
5155 			function done() {
5156 				dom.remove(id);
5157 
5158 				if (elm) {
5159 					elm.onreadystatechange = elm.onload = elm = null;
5160 				}
5161 
5162 				callback();
5163 			}
5164 
5165 			function error() {
5166 				/*eslint no-console:0 */
5167 
5168 				// Report the error so it's easier for people to spot loading errors
5169 				if (typeof(console) !== "undefined" && console.log) {
5170 					console.log("Failed to load: " + url);
5171 				}
5172 
5173 				// We can't mark it as done if there is a load error since
5174 				// A) We don't want to produce 404 errors on the server and
5175 				// B) the onerror event won't fire on all browsers.
5176 				// done();
5177 			}
5178 
5179 			id = dom.uniqueId();
5180 
5181 			// Create new script element
5182 			elm = document.createElement('script');
5183 			elm.id = id;
5184 			elm.type = 'text/javascript';
5185 			elm.src = url;
5186 
5187 			// Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
5188 			if ("onreadystatechange" in elm) {
5189 				elm.onreadystatechange = function() {
5190 					if (/loaded|complete/.test(elm.readyState)) {
5191 						done();
5192 					}
5193 				};
5194 			} else {
5195 				elm.onload = done;
5196 			}
5197 
5198 			// Add onerror event will get fired on some browsers but not all of them
5199 			elm.onerror = error;
5200 
5201 			// Add script to document
5202 			(document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
5203 		}
5204 
5205 		/**
5206 		 * Returns true/false if a script has been loaded or not.
5207 		 *
5208 		 * @method isDone
5209 		 * @param {String} url URL to check for.
5210 		 * @return {Boolean} true/false if the URL is loaded.
5211 		 */
5212 		this.isDone = function(url) {
5213 			return states[url] == LOADED;
5214 		};
5215 
5216 		/**
5217 		 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
5218 		 * the script loader or to skip it from loading some script.
5219 		 *
5220 		 * @method markDone
5221 		 * @param {string} u Absolute URL to the script to mark as loaded.
5222 		 */
5223 		this.markDone = function(url) {
5224 			states[url] = LOADED;
5225 		};
5226 
5227 		/**
5228 		 * Adds a specific script to the load queue of the script loader.
5229 		 *
5230 		 * @method add
5231 		 * @param {String} url Absolute URL to script to add.
5232 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
5233 		 * @param {Object} scope Optional scope to execute callback in.
5234 		 */
5235 		this.add = this.load = function(url, callback, scope) {
5236 			var state = states[url];
5237 
5238 			// Add url to load queue
5239 			if (state == undef) {
5240 				queue.push(url);
5241 				states[url] = QUEUED;
5242 			}
5243 
5244 			if (callback) {
5245 				// Store away callback for later execution
5246 				if (!scriptLoadedCallbacks[url]) {
5247 					scriptLoadedCallbacks[url] = [];
5248 				}
5249 
5250 				scriptLoadedCallbacks[url].push({
5251 					func: callback,
5252 					scope: scope || this
5253 				});
5254 			}
5255 		};
5256 
5257 		/**
5258 		 * Starts the loading of the queue.
5259 		 *
5260 		 * @method loadQueue
5261 		 * @param {function} callback Optional callback to execute when all queued items are loaded.
5262 		 * @param {Object} scope Optional scope to execute the callback in.
5263 		 */
5264 		this.loadQueue = function(callback, scope) {
5265 			this.loadScripts(queue, callback, scope);
5266 		};
5267 
5268 		/**
5269 		 * Loads the specified queue of files and executes the callback ones they are loaded.
5270 		 * This method is generally not used outside this class but it might be useful in some scenarios.
5271 		 *
5272 		 * @method loadScripts
5273 		 * @param {Array} scripts Array of queue items to load.
5274 		 * @param {function} callback Optional callback to execute ones all items are loaded.
5275 		 * @param {Object} scope Optional scope to execute callback in.
5276 		 */
5277 		this.loadScripts = function(scripts, callback, scope) {
5278 			var loadScripts;
5279 
5280 			function execScriptLoadedCallbacks(url) {
5281 				// Execute URL callback functions
5282 				each(scriptLoadedCallbacks[url], function(callback) {
5283 					callback.func.call(callback.scope);
5284 				});
5285 
5286 				scriptLoadedCallbacks[url] = undef;
5287 			}
5288 
5289 			queueLoadedCallbacks.push({
5290 				func: callback,
5291 				scope: scope || this
5292 			});
5293 
5294 			loadScripts = function() {
5295 				var loadingScripts = grep(scripts);
5296 
5297 				// Current scripts has been handled
5298 				scripts.length = 0;
5299 
5300 				// Load scripts that needs to be loaded
5301 				each(loadingScripts, function(url) {
5302 					// Script is already loaded then execute script callbacks directly
5303 					if (states[url] == LOADED) {
5304 						execScriptLoadedCallbacks(url);
5305 						return;
5306 					}
5307 
5308 					// Is script not loading then start loading it
5309 					if (states[url] != LOADING) {
5310 						states[url] = LOADING;
5311 						loading++;
5312 
5313 						loadScript(url, function() {
5314 							states[url] = LOADED;
5315 							loading--;
5316 
5317 							execScriptLoadedCallbacks(url);
5318 
5319 							// Load more scripts if they where added by the recently loaded script
5320 							loadScripts();
5321 						});
5322 					}
5323 				});
5324 
5325 				// No scripts are currently loading then execute all pending queue loaded callbacks
5326 				if (!loading) {
5327 					each(queueLoadedCallbacks, function(callback) {
5328 						callback.func.call(callback.scope);
5329 					});
5330 
5331 					queueLoadedCallbacks.length = 0;
5332 				}
5333 			};
5334 
5335 			loadScripts();
5336 		};
5337 	}
5338 
5339 	ScriptLoader.ScriptLoader = new ScriptLoader();
5340 
5341 	return ScriptLoader;
5342 });
5343 
5344 // Included from: js/tinymce/classes/AddOnManager.js
5345 
5346 /**
5347  * AddOnManager.js
5348  *
5349  * Copyright, Moxiecode Systems AB
5350  * Released under LGPL License.
5351  *
5352  * License: http://www.tinymce.com/license
5353  * Contributing: http://www.tinymce.com/contributing
5354  */
5355 
5356 /**
5357  * This class handles the loading of themes/plugins or other add-ons and their language packs.
5358  *
5359  * @class tinymce.AddOnManager
5360  */
5361 define("tinymce/AddOnManager", [
5362 	"tinymce/dom/ScriptLoader",
5363 	"tinymce/util/Tools"
5364 ], function(ScriptLoader, Tools) {
5365 	var each = Tools.each;
5366 
5367 	function AddOnManager() {
5368 		var self = this;
5369 
5370 		self.items = [];
5371 		self.urls = {};
5372 		self.lookup = {};
5373 	}
5374 
5375 	AddOnManager.prototype = {
5376 		/**
5377 		 * Returns the specified add on by the short name.
5378 		 *
5379 		 * @method get
5380 		 * @param {String} name Add-on to look for.
5381 		 * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
5382 		 */
5383 		get: function(name) {
5384 			if (this.lookup[name]) {
5385 				return this.lookup[name].instance;
5386 			} else {
5387 				return undefined;
5388 			}
5389 		},
5390 
5391 		dependencies: function(name) {
5392 			var result;
5393 
5394 			if (this.lookup[name]) {
5395 				result = this.lookup[name].dependencies;
5396 			}
5397 
5398 			return result || [];
5399 		},
5400 
5401 		/**
5402 		 * Loads a language pack for the specified add-on.
5403 		 *
5404 		 * @method requireLangPack
5405 		 * @param {String} name Short name of the add-on.
5406 		 * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
5407 		 */
5408 		requireLangPack: function(name, languages) {
5409 			var language = AddOnManager.language;
5410 
5411 			if (language && AddOnManager.languageLoad !== false) {
5412 				if (languages) {
5413 					languages = ',' + languages + ',';
5414 
5415 					// Load short form sv.js or long form sv_SE.js
5416 					if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) {
5417 						language = language.substr(0, 2);
5418 					} else if (languages.indexOf(',' + language + ',') == -1) {
5419 						return;
5420 					}
5421 				}
5422 
5423 				ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js');
5424 			}
5425 		},
5426 
5427 		/**
5428 		 * Adds a instance of the add-on by it's short name.
5429 		 *
5430 		 * @method add
5431 		 * @param {String} id Short name/id for the add-on.
5432 		 * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
5433 		 * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
5434 		 * @example
5435 		 * // Create a simple plugin
5436 		 * tinymce.create('tinymce.plugins.TestPlugin', {
5437 		 *   TestPlugin: function(ed, url) {
5438 		 *   ed.on('click', function(e) {
5439 		 *      ed.windowManager.alert('Hello World!');
5440 		 *   });
5441 		 *   }
5442 		 * });
5443 		 *
5444 		 * // Register plugin using the add method
5445 		 * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
5446 		 *
5447 		 * // Initialize TinyMCE
5448 		 * tinymce.init({
5449 		 *  ...
5450 		 *  plugins: '-test' // Init the plugin but don't try to load it
5451 		 * });
5452 		 */
5453 		add: function(id, addOn, dependencies) {
5454 			this.items.push(addOn);
5455 			this.lookup[id] = {instance: addOn, dependencies: dependencies};
5456 
5457 			return addOn;
5458 		},
5459 
5460 		createUrl: function(baseUrl, dep) {
5461 			if (typeof dep === "object") {
5462 				return dep;
5463 			} else {
5464 				return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
5465 			}
5466 		},
5467 
5468 		/**
5469 		 * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
5470 		 * This should be used in development mode.  A new compressor/javascript munger process will ensure that the
5471 		 * components are put together into the plugin.js file and compressed correctly.
5472 		 *
5473 		 * @method addComponents
5474 		 * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
5475 		 * @param {Array} scripts Array containing the names of the scripts to load.
5476 		 */
5477 		addComponents: function(pluginName, scripts) {
5478 			var pluginUrl = this.urls[pluginName];
5479 
5480 			each(scripts, function(script) {
5481 				ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
5482 			});
5483 		},
5484 
5485 		/**
5486 		 * Loads an add-on from a specific url.
5487 		 *
5488 		 * @method load
5489 		 * @param {String} name Short name of the add-on that gets loaded.
5490 		 * @param {String} addOnUrl URL to the add-on that will get loaded.
5491 		 * @param {function} callback Optional callback to execute ones the add-on is loaded.
5492 		 * @param {Object} scope Optional scope to execute the callback in.
5493 		 * @example
5494 		 * // Loads a plugin from an external URL
5495 		 * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
5496 		 *
5497 		 * // Initialize TinyMCE
5498 		 * tinymce.init({
5499 		 *  ...
5500 		 *  plugins: '-myplugin' // Don't try to load it again
5501 		 * });
5502 		 */
5503 		load: function(name, addOnUrl, callback, scope) {
5504 			var self = this, url = addOnUrl;
5505 
5506 			function loadDependencies() {
5507 				var dependencies = self.dependencies(name);
5508 
5509 				each(dependencies, function(dep) {
5510 					var newUrl = self.createUrl(addOnUrl, dep);
5511 
5512 					self.load(newUrl.resource, newUrl, undefined, undefined);
5513 				});
5514 
5515 				if (callback) {
5516 					if (scope) {
5517 						callback.call(scope);
5518 					} else {
5519 						callback.call(ScriptLoader);
5520 					}
5521 				}
5522 			}
5523 
5524 			if (self.urls[name]) {
5525 				return;
5526 			}
5527 
5528 			if (typeof addOnUrl === "object") {
5529 				url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;
5530 			}
5531 
5532 			if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
5533 				url = AddOnManager.baseURL + '/' + url;
5534 			}
5535 
5536 			self.urls[name] = url.substring(0, url.lastIndexOf('/'));
5537 
5538 			if (self.lookup[name]) {
5539 				loadDependencies();
5540 			} else {
5541 				ScriptLoader.ScriptLoader.add(url, loadDependencies, scope);
5542 			}
5543 		}
5544 	};
5545 
5546 	AddOnManager.PluginManager = new AddOnManager();
5547 	AddOnManager.ThemeManager = new AddOnManager();
5548 
5549 	return AddOnManager;
5550 });
5551 
5552 /**
5553  * TinyMCE theme class.
5554  *
5555  * @class tinymce.Theme
5556  */
5557 
5558 /**
5559  * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
5560  *
5561  * @method renderUI
5562  * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
5563  * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
5564  */
5565 
5566 /**
5567  * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional.
5568  *
5569  * @class tinymce.Plugin
5570  * @example
5571  * tinymce.PluginManager.add('example', function(editor, url) {
5572  *     // Add a button that opens a window
5573  *     editor.addButton('example', {
5574  *         text: 'My button',
5575  *         icon: false,
5576  *         onclick: function() {
5577  *             // Open window
5578  *             editor.windowManager.open({
5579  *                 title: 'Example plugin',
5580  *                 body: [
5581  *                     {type: 'textbox', name: 'title', label: 'Title'}
5582  *                 ],
5583  *                 onsubmit: function(e) {
5584  *                     // Insert content when the window form is submitted
5585  *                     editor.insertContent('Title: ' + e.data.title);
5586  *                 }
5587  *             });
5588  *         }
5589  *     });
5590  *
5591  *     // Adds a menu item to the tools menu
5592  *     editor.addMenuItem('example', {
5593  *         text: 'Example plugin',
5594  *         context: 'tools',
5595  *         onclick: function() {
5596  *             // Open window with a specific url
5597  *             editor.windowManager.open({
5598  *                 title: 'TinyMCE site',
5599  *                 url: 'http://www.tinymce.com',
5600  *                 width: 800,
5601  *                 height: 600,
5602  *                 buttons: [{
5603  *                     text: 'Close',
5604  *                     onclick: 'close'
5605  *                 }]
5606  *             });
5607  *         }
5608  *     });
5609  * });
5610  */
5611 
5612 // Included from: js/tinymce/classes/html/Node.js
5613 
5614 /**
5615  * Node.js
5616  *
5617  * Copyright, Moxiecode Systems AB
5618  * Released under LGPL License.
5619  *
5620  * License: http://www.tinymce.com/license
5621  * Contributing: http://www.tinymce.com/contributing
5622  */
5623 
5624 /**
5625  * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
5626  *
5627  * @example
5628  * var node = new tinymce.html.Node('strong', 1);
5629  * someRoot.append(node);
5630  *
5631  * @class tinymce.html.Node
5632  * @version 3.4
5633  */
5634 define("tinymce/html/Node", [], function() {
5635 	var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
5636 		'#text': 3,
5637 		'#comment': 8,
5638 		'#cdata': 4,
5639 		'#pi': 7,
5640 		'#doctype': 10,
5641 		'#document-fragment': 11
5642 	};
5643 
5644 	// Walks the tree left/right
5645 	function walk(node, root_node, prev) {
5646 		var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
5647 
5648 		// Walk into nodes if it has a start
5649 		if (node[startName]) {
5650 			return node[startName];
5651 		}
5652 
5653 		// Return the sibling if it has one
5654 		if (node !== root_node) {
5655 			sibling = node[siblingName];
5656 
5657 			if (sibling) {
5658 				return sibling;
5659 			}
5660 
5661 			// Walk up the parents to look for siblings
5662 			for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
5663 				sibling = parent[siblingName];
5664 
5665 				if (sibling) {
5666 					return sibling;
5667 				}
5668 			}
5669 		}
5670 	}
5671 
5672 	/**
5673 	 * Constructs a new Node instance.
5674 	 *
5675 	 * @constructor
5676 	 * @method Node
5677 	 * @param {String} name Name of the node type.
5678 	 * @param {Number} type Numeric type representing the node.
5679 	 */
5680 	function Node(name, type) {
5681 		this.name = name;
5682 		this.type = type;
5683 
5684 		if (type === 1) {
5685 			this.attributes = [];
5686 			this.attributes.map = {};
5687 		}
5688 	}
5689 
5690 	Node.prototype = {
5691 		/**
5692 		 * Replaces the current node with the specified one.
5693 		 *
5694 		 * @example
5695 		 * someNode.replace(someNewNode);
5696 		 *
5697 		 * @method replace
5698 		 * @param {tinymce.html.Node} node Node to replace the current node with.
5699 		 * @return {tinymce.html.Node} The old node that got replaced.
5700 		 */
5701 		replace: function(node) {
5702 			var self = this;
5703 
5704 			if (node.parent) {
5705 				node.remove();
5706 			}
5707 
5708 			self.insert(node, self);
5709 			self.remove();
5710 
5711 			return self;
5712 		},
5713 
5714 		/**
5715 		 * Gets/sets or removes an attribute by name.
5716 		 *
5717 		 * @example
5718 		 * someNode.attr("name", "value"); // Sets an attribute
5719 		 * console.log(someNode.attr("name")); // Gets an attribute
5720 		 * someNode.attr("name", null); // Removes an attribute
5721 		 *
5722 		 * @method attr
5723 		 * @param {String} name Attribute name to set or get.
5724 		 * @param {String} value Optional value to set.
5725 		 * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
5726 		 */
5727 		attr: function(name, value) {
5728 			var self = this, attrs, i, undef;
5729 
5730 			if (typeof name !== "string") {
5731 				for (i in name) {
5732 					self.attr(i, name[i]);
5733 				}
5734 
5735 				return self;
5736 			}
5737 
5738 			if ((attrs = self.attributes)) {
5739 				if (value !== undef) {
5740 					// Remove attribute
5741 					if (value === null) {
5742 						if (name in attrs.map) {
5743 							delete attrs.map[name];
5744 
5745 							i = attrs.length;
5746 							while (i--) {
5747 								if (attrs[i].name === name) {
5748 									attrs = attrs.splice(i, 1);
5749 									return self;
5750 								}
5751 							}
5752 						}
5753 
5754 						return self;
5755 					}
5756 
5757 					// Set attribute
5758 					if (name in attrs.map) {
5759 						// Set attribute
5760 						i = attrs.length;
5761 						while (i--) {
5762 							if (attrs[i].name === name) {
5763 								attrs[i].value = value;
5764 								break;
5765 							}
5766 						}
5767 					} else {
5768 						attrs.push({name: name, value: value});
5769 					}
5770 
5771 					attrs.map[name] = value;
5772 
5773 					return self;
5774 				} else {
5775 					return attrs.map[name];
5776 				}
5777 			}
5778 		},
5779 
5780 		/**
5781 		 * Does a shallow clones the node into a new node. It will also exclude id attributes since
5782 		 * there should only be one id per document.
5783 		 *
5784 		 * @example
5785 		 * var clonedNode = node.clone();
5786 		 *
5787 		 * @method clone
5788 		 * @return {tinymce.html.Node} New copy of the original node.
5789 		 */
5790 		clone: function() {
5791 			var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
5792 
5793 			// Clone element attributes
5794 			if ((selfAttrs = self.attributes)) {
5795 				cloneAttrs = [];
5796 				cloneAttrs.map = {};
5797 
5798 				for (i = 0, l = selfAttrs.length; i < l; i++) {
5799 					selfAttr = selfAttrs[i];
5800 
5801 					// Clone everything except id
5802 					if (selfAttr.name !== 'id') {
5803 						cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
5804 						cloneAttrs.map[selfAttr.name] = selfAttr.value;
5805 					}
5806 				}
5807 
5808 				clone.attributes = cloneAttrs;
5809 			}
5810 
5811 			clone.value = self.value;
5812 			clone.shortEnded = self.shortEnded;
5813 
5814 			return clone;
5815 		},
5816 
5817 		/**
5818 		 * Wraps the node in in another node.
5819 		 *
5820 		 * @example
5821 		 * node.wrap(wrapperNode);
5822 		 *
5823 		 * @method wrap
5824 		 */
5825 		wrap: function(wrapper) {
5826 			var self = this;
5827 
5828 			self.parent.insert(wrapper, self);
5829 			wrapper.append(self);
5830 
5831 			return self;
5832 		},
5833 
5834 		/**
5835 		 * Unwraps the node in other words it removes the node but keeps the children.
5836 		 *
5837 		 * @example
5838 		 * node.unwrap();
5839 		 *
5840 		 * @method unwrap
5841 		 */
5842 		unwrap: function() {
5843 			var self = this, node, next;
5844 
5845 			for (node = self.firstChild; node; ) {
5846 				next = node.next;
5847 				self.insert(node, self, true);
5848 				node = next;
5849 			}
5850 
5851 			self.remove();
5852 		},
5853 
5854 		/**
5855 		 * Removes the node from it's parent.
5856 		 *
5857 		 * @example
5858 		 * node.remove();
5859 		 *
5860 		 * @method remove
5861 		 * @return {tinymce.html.Node} Current node that got removed.
5862 		 */
5863 		remove: function() {
5864 			var self = this, parent = self.parent, next = self.next, prev = self.prev;
5865 
5866 			if (parent) {
5867 				if (parent.firstChild === self) {
5868 					parent.firstChild = next;
5869 
5870 					if (next) {
5871 						next.prev = null;
5872 					}
5873 				} else {
5874 					prev.next = next;
5875 				}
5876 
5877 				if (parent.lastChild === self) {
5878 					parent.lastChild = prev;
5879 
5880 					if (prev) {
5881 						prev.next = null;
5882 					}
5883 				} else {
5884 					next.prev = prev;
5885 				}
5886 
5887 				self.parent = self.next = self.prev = null;
5888 			}
5889 
5890 			return self;
5891 		},
5892 
5893 		/**
5894 		 * Appends a new node as a child of the current node.
5895 		 *
5896 		 * @example
5897 		 * node.append(someNode);
5898 		 *
5899 		 * @method append
5900 		 * @param {tinymce.html.Node} node Node to append as a child of the current one.
5901 		 * @return {tinymce.html.Node} The node that got appended.
5902 		 */
5903 		append: function(node) {
5904 			var self = this, last;
5905 
5906 			if (node.parent) {
5907 				node.remove();
5908 			}
5909 
5910 			last = self.lastChild;
5911 			if (last) {
5912 				last.next = node;
5913 				node.prev = last;
5914 				self.lastChild = node;
5915 			} else {
5916 				self.lastChild = self.firstChild = node;
5917 			}
5918 
5919 			node.parent = self;
5920 
5921 			return node;
5922 		},
5923 
5924 		/**
5925 		 * Inserts a node at a specific position as a child of the current node.
5926 		 *
5927 		 * @example
5928 		 * parentNode.insert(newChildNode, oldChildNode);
5929 		 *
5930 		 * @method insert
5931 		 * @param {tinymce.html.Node} node Node to insert as a child of the current node.
5932 		 * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
5933 		 * @param {Boolean} before Optional state to insert the node before the reference node.
5934 		 * @return {tinymce.html.Node} The node that got inserted.
5935 		 */
5936 		insert: function(node, ref_node, before) {
5937 			var parent;
5938 
5939 			if (node.parent) {
5940 				node.remove();
5941 			}
5942 
5943 			parent = ref_node.parent || this;
5944 
5945 			if (before) {
5946 				if (ref_node === parent.firstChild) {
5947 					parent.firstChild = node;
5948 				} else {
5949 					ref_node.prev.next = node;
5950 				}
5951 
5952 				node.prev = ref_node.prev;
5953 				node.next = ref_node;
5954 				ref_node.prev = node;
5955 			} else {
5956 				if (ref_node === parent.lastChild) {
5957 					parent.lastChild = node;
5958 				} else {
5959 					ref_node.next.prev = node;
5960 				}
5961 
5962 				node.next = ref_node.next;
5963 				node.prev = ref_node;
5964 				ref_node.next = node;
5965 			}
5966 
5967 			node.parent = parent;
5968 
5969 			return node;
5970 		},
5971 
5972 		/**
5973 		 * Get all children by name.
5974 		 *
5975 		 * @method getAll
5976 		 * @param {String} name Name of the child nodes to collect.
5977 		 * @return {Array} Array with child nodes matchin the specified name.
5978 		 */
5979 		getAll: function(name) {
5980 			var self = this, node, collection = [];
5981 
5982 			for (node = self.firstChild; node; node = walk(node, self)) {
5983 				if (node.name === name) {
5984 					collection.push(node);
5985 				}
5986 			}
5987 
5988 			return collection;
5989 		},
5990 
5991 		/**
5992 		 * Removes all children of the current node.
5993 		 *
5994 		 * @method empty
5995 		 * @return {tinymce.html.Node} The current node that got cleared.
5996 		 */
5997 		empty: function() {
5998 			var self = this, nodes, i, node;
5999 
6000 			// Remove all children
6001 			if (self.firstChild) {
6002 				nodes = [];
6003 
6004 				// Collect the children
6005 				for (node = self.firstChild; node; node = walk(node, self)) {
6006 					nodes.push(node);
6007 				}
6008 
6009 				// Remove the children
6010 				i = nodes.length;
6011 				while (i--) {
6012 					node = nodes[i];
6013 					node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
6014 				}
6015 			}
6016 
6017 			self.firstChild = self.lastChild = null;
6018 
6019 			return self;
6020 		},
6021 
6022 		/**
6023 		 * Returns true/false if the node is to be considered empty or not.
6024 		 *
6025 		 * @example
6026 		 * node.isEmpty({img: true});
6027 		 * @method isEmpty
6028 		 * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
6029 		 * @return {Boolean} true/false if the node is empty or not.
6030 		 */
6031 		isEmpty: function(elements) {
6032 			var self = this, node = self.firstChild, i, name;
6033 
6034 			if (node) {
6035 				do {
6036 					if (node.type === 1) {
6037 						// Ignore bogus elements
6038 						if (node.attributes.map['data-mce-bogus']) {
6039 							continue;
6040 						}
6041 
6042 						// Keep empty elements like <img />
6043 						if (elements[node.name]) {
6044 							return false;
6045 						}
6046 
6047 						// Keep elements with data attributes or name attribute like <a name="1"></a>
6048 						i = node.attributes.length;
6049 						while (i--) {
6050 							name = node.attributes[i].name;
6051 							if (name === "name" || name.indexOf('data-mce-') === 0) {
6052 								return false;
6053 							}
6054 						}
6055 					}
6056 
6057 					// Keep comments
6058 					if (node.type === 8) {
6059 						return false;
6060 					}
6061 
6062 					// Keep non whitespace text nodes
6063 					if ((node.type === 3 && !whiteSpaceRegExp.test(node.value))) {
6064 						return false;
6065 					}
6066 				} while ((node = walk(node, self)));
6067 			}
6068 
6069 			return true;
6070 		},
6071 
6072 		/**
6073 		 * Walks to the next or previous node and returns that node or null if it wasn't found.
6074 		 *
6075 		 * @method walk
6076 		 * @param {Boolean} prev Optional previous node state defaults to false.
6077 		 * @return {tinymce.html.Node} Node that is next to or previous of the current node.
6078 		 */
6079 		walk: function(prev) {
6080 			return walk(this, null, prev);
6081 		}
6082 	};
6083 
6084 	/**
6085 	 * Creates a node of a specific type.
6086 	 *
6087 	 * @static
6088 	 * @method create
6089 	 * @param {String} name Name of the node type to create for example "b" or "#text".
6090 	 * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
6091 	 */
6092 	Node.create = function(name, attrs) {
6093 		var node, attrName;
6094 
6095 		// Create node
6096 		node = new Node(name, typeLookup[name] || 1);
6097 
6098 		// Add attributes if needed
6099 		if (attrs) {
6100 			for (attrName in attrs) {
6101 				node.attr(attrName, attrs[attrName]);
6102 			}
6103 		}
6104 
6105 		return node;
6106 	};
6107 
6108 	return Node;
6109 });
6110 
6111 // Included from: js/tinymce/classes/html/Schema.js
6112 
6113 /**
6114  * Schema.js
6115  *
6116  * Copyright, Moxiecode Systems AB
6117  * Released under LGPL License.
6118  *
6119  * License: http://www.tinymce.com/license
6120  * Contributing: http://www.tinymce.com/contributing
6121  */
6122 
6123 /**
6124  * Schema validator class.
6125  *
6126  * @class tinymce.html.Schema
6127  * @example
6128  *  if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
6129  *    alert('span is valid child of p.');
6130  *
6131  *  if (tinymce.activeEditor.schema.getElementRule('p'))
6132  *    alert('P is a valid element.');
6133  *
6134  * @class tinymce.html.Schema
6135  * @version 3.4
6136  */
6137 define("tinymce/html/Schema", [
6138 	"tinymce/util/Tools"
6139 ], function(Tools) {
6140 	var mapCache = {};
6141 	var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
6142 
6143 	function split(items, delim) {
6144 		return items ? items.split(delim || ' ') : [];
6145 	}
6146 
6147 	/**
6148 	 * Builds a schema lookup table
6149 	 *
6150 	 * @private
6151 	 * @param {String} type html4, html5 or html5-strict schema type.
6152 	 * @return {Object} Schema lookup table.
6153 	 */
6154 	function compileSchema(type) {
6155 		var schema = {}, globalAttributes, blockContent;
6156 		var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
6157 
6158 		function add(name, attributes, children) {
6159 			var ni, i, attributesOrder, args = arguments;
6160 
6161 			function arrayToMap(array) {
6162 				var map = {}, i, l;
6163 
6164 				for (i = 0, l = array.length; i < l; i++) {
6165 					map[array[i]] = {};
6166 				}
6167 
6168 				return map;
6169 			}
6170 
6171 			children = children || [];
6172 			attributes = attributes || "";
6173 
6174 			if (typeof(children) === "string") {
6175 				children = split(children);
6176 			}
6177 
6178 			// Split string children
6179 			for (i = 3; i < args.length; i++) {
6180 				if (typeof(args[i]) === "string") {
6181 					args[i] = split(args[i]);
6182 				}
6183 
6184 				children.push.apply(children, args[i]);
6185 			}
6186 
6187 			name = split(name);
6188 			ni = name.length;
6189 			while (ni--) {
6190 				attributesOrder = [].concat(globalAttributes, split(attributes));
6191 				schema[name[ni]] = {
6192 					attributes: arrayToMap(attributesOrder),
6193 					attributesOrder: attributesOrder,
6194 					children: arrayToMap(children)
6195 				};
6196 			}
6197 		}
6198 
6199 		function addAttrs(name, attributes) {
6200 			var ni, schemaItem, i, l;
6201 
6202 			name = split(name);
6203 			ni = name.length;
6204 			attributes = split(attributes);
6205 			while (ni--) {
6206 				schemaItem = schema[name[ni]];
6207 				for (i = 0, l = attributes.length; i < l; i++) {
6208 					schemaItem.attributes[attributes[i]] = {};
6209 					schemaItem.attributesOrder.push(attributes[i]);
6210 				}
6211 			}
6212 		}
6213 
6214 		// Use cached schema
6215 		if (mapCache[type]) {
6216 			return mapCache[type];
6217 		}
6218 
6219 		// Attributes present on all elements
6220 		globalAttributes = split("id accesskey class dir lang style tabindex title");
6221 
6222 		// Event attributes can be opt-in/opt-out
6223 		/*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
6224 				"ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
6225 				"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
6226 				"onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
6227 				"onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
6228 				"onwaiting"
6229 		);*/
6230 
6231 		// Block content elements
6232 		blockContent = split(
6233 			"address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
6234 		);
6235 
6236 		// Phrasing content elements from the HTML5 spec (inline)
6237 		phrasingContent = split(
6238 			"a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
6239 			"label map noscript object q s samp script select small span strong sub sup " +
6240 			"textarea u var #text #comment"
6241 		);
6242 
6243 		// Add HTML5 items to globalAttributes, blockContent, phrasingContent
6244 		if (type != "html4") {
6245 			globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
6246 				"hidden spellcheck translate"));
6247 			blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
6248 			phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output progress time wbr " +
6249 				"video ruby bdi keygen"));
6250 		}
6251 
6252 		// Add HTML4 elements unless it's html5-strict
6253 		if (type != "html5-strict") {
6254 			globalAttributes.push("xml:lang");
6255 
6256 			html4PhrasingContent = split("acronym applet basefont big font strike tt");
6257 			phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
6258 
6259 			each(html4PhrasingContent, function(name) {
6260 				add(name, "", phrasingContent);
6261 			});
6262 
6263 			html4BlockContent = split("center dir isindex noframes");
6264 			blockContent.push.apply(blockContent, html4BlockContent);
6265 
6266 			// Flow content elements from the HTML5 spec (block+inline)
6267 			flowContent = [].concat(blockContent, phrasingContent);
6268 
6269 			each(html4BlockContent, function(name) {
6270 				add(name, "", flowContent);
6271 			});
6272 		}
6273 
6274 		// Flow content elements from the HTML5 spec (block+inline)
6275 		flowContent = flowContent || [].concat(blockContent, phrasingContent);
6276 
6277 		// HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
6278 		// Schema items <element name>, <specific attributes>, <children ..>
6279 		add("html", "manifest", "head body");
6280 		add("head", "", "base command link meta noscript script style title");
6281 		add("title hr noscript br");
6282 		add("base", "href target");
6283 		add("link", "href rel media hreflang type sizes hreflang");
6284 		add("meta", "name http-equiv content charset");
6285 		add("style", "media type scoped");
6286 		add("script", "src async defer type charset");
6287 		add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
6288 				"onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
6289 				"onpopstate onresize onscroll onstorage onunload", flowContent);
6290 		add("address dt dd div caption", "", flowContent);
6291 		add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
6292 		add("blockquote", "cite", flowContent);
6293 		add("ol", "reversed start type", "li");
6294 		add("ul", "", "li");
6295 		add("li", "value", flowContent);
6296 		add("dl", "", "dt dd");
6297 		add("a", "href target rel media hreflang type", phrasingContent);
6298 		add("q", "cite", phrasingContent);
6299 		add("ins del", "cite datetime", flowContent);
6300 		add("img", "src alt usemap ismap width height");
6301 		add("iframe", "src name width height", flowContent);
6302 		add("embed", "src type width height");
6303 		add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
6304 		add("param", "name value");
6305 		add("map", "name", flowContent, "area");
6306 		add("area", "alt coords shape href target rel media hreflang type");
6307 		add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
6308 		add("colgroup", "span", "col");
6309 		add("col", "span");
6310 		add("tbody thead tfoot", "", "tr");
6311 		add("tr", "", "td th");
6312 		add("td", "colspan rowspan headers", flowContent);
6313 		add("th", "colspan rowspan headers scope abbr", flowContent);
6314 		add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
6315 		add("fieldset", "disabled form name", flowContent, "legend");
6316 		add("label", "form for", phrasingContent);
6317 		add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
6318 				"formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
6319 		);
6320 		add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
6321 			type == "html4" ? flowContent : phrasingContent);
6322 		add("select", "disabled form multiple name required size", "option optgroup");
6323 		add("optgroup", "disabled label", "option");
6324 		add("option", "disabled label selected value");
6325 		add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
6326 		add("menu", "type label", flowContent, "li");
6327 		add("noscript", "", flowContent);
6328 
6329 		// Extend with HTML5 elements
6330 		if (type != "html4") {
6331 			add("wbr");
6332 			add("ruby", "", phrasingContent, "rt rp");
6333 			add("figcaption", "", flowContent);
6334 			add("mark rt rp summary bdi", "", phrasingContent);
6335 			add("canvas", "width height", flowContent);
6336 			add("video", "src crossorigin poster preload autoplay mediagroup loop " +
6337 				"muted controls width height buffered", flowContent, "track source");
6338 			add("audio", "src crossorigin preload autoplay mediagroup loop muted controls buffered volume", flowContent, "track source");
6339 			add("source", "src type media");
6340 			add("track", "kind src srclang label default");
6341 			add("datalist", "", phrasingContent, "option");
6342 			add("article section nav aside header footer", "", flowContent);
6343 			add("hgroup", "", "h1 h2 h3 h4 h5 h6");
6344 			add("figure", "", flowContent, "figcaption");
6345 			add("time", "datetime", phrasingContent);
6346 			add("dialog", "open", flowContent);
6347 			add("command", "type label icon disabled checked radiogroup command");
6348 			add("output", "for form name", phrasingContent);
6349 			add("progress", "value max", phrasingContent);
6350 			add("meter", "value min max low high optimum", phrasingContent);
6351 			add("details", "open", flowContent, "summary");
6352 			add("keygen", "autofocus challenge disabled form keytype name");
6353 		}
6354 
6355 		// Extend with HTML4 attributes unless it's html5-strict
6356 		if (type != "html5-strict") {
6357 			addAttrs("script", "language xml:space");
6358 			addAttrs("style", "xml:space");
6359 			addAttrs("object", "declare classid codebase codetype archive standby align border hspace vspace");
6360 			addAttrs("param", "valuetype type");
6361 			addAttrs("a", "charset name rev shape coords");
6362 			addAttrs("br", "clear");
6363 			addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
6364 			addAttrs("img", "name longdesc align border hspace vspace");
6365 			addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
6366 			addAttrs("font basefont", "size color face");
6367 			addAttrs("input", "usemap align");
6368 			addAttrs("select", "onchange");
6369 			addAttrs("textarea");
6370 			addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
6371 			addAttrs("ul", "type compact");
6372 			addAttrs("li", "type");
6373 			addAttrs("ol dl menu dir", "compact");
6374 			addAttrs("pre", "width xml:space");
6375 			addAttrs("hr", "align noshade size width");
6376 			addAttrs("isindex", "prompt");
6377 			addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
6378 			addAttrs("col", "width align char charoff valign");
6379 			addAttrs("colgroup", "width align char charoff valign");
6380 			addAttrs("thead", "align char charoff valign");
6381 			addAttrs("tr", "align char charoff valign bgcolor");
6382 			addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
6383 			addAttrs("form", "accept");
6384 			addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
6385 			addAttrs("tfoot", "align char charoff valign");
6386 			addAttrs("tbody", "align char charoff valign");
6387 			addAttrs("area", "nohref");
6388 			addAttrs("body", "background bgcolor text link vlink alink");
6389 		}
6390 
6391 		// Extend with HTML5 attributes unless it's html4
6392 		if (type != "html4") {
6393 			addAttrs("input button select textarea", "autofocus");
6394 			addAttrs("input textarea", "placeholder");
6395 			addAttrs("a", "download");
6396 			addAttrs("link script img", "crossorigin");
6397 			addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc
6398 		}
6399 
6400 		// Special: iframe, ruby, video, audio, label
6401 
6402 		// Delete children of the same name from it's parent
6403 		// For example: form can't have a child of the name form
6404 		each(split('a form meter progress dfn'), function(name) {
6405 			if (schema[name]) {
6406 				delete schema[name].children[name];
6407 			}
6408 		});
6409 
6410 		// Delete header, footer, sectioning and heading content descendants
6411 		/*each('dt th address', function(name) {
6412 			delete schema[name].children[name];
6413 		});*/
6414 
6415 		// Caption can't have tables
6416 		delete schema.caption.children.table;
6417 
6418 		// TODO: LI:s can only have value if parent is OL
6419 
6420 		// TODO: Handle transparent elements
6421 		// a ins del canvas map
6422 
6423 		mapCache[type] = schema;
6424 
6425 		return schema;
6426 	}
6427 
6428 	/**
6429 	 * Constructs a new Schema instance.
6430 	 *
6431 	 * @constructor
6432 	 * @method Schema
6433 	 * @param {Object} settings Name/value settings object.
6434 	 */
6435 	return function(settings) {
6436 		var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
6437 		var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap;
6438 		var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, customElementsMap = {}, specialElements = {};
6439 
6440 		// Creates an lookup table map object for the specified option or the default value
6441 		function createLookupTable(option, default_value, extendWith) {
6442 			var value = settings[option];
6443 
6444 			if (!value) {
6445 				// Get cached default map or make it if needed
6446 				value = mapCache[option];
6447 
6448 				if (!value) {
6449 					value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
6450 					value = extend(value, extendWith);
6451 
6452 					mapCache[option] = value;
6453 				}
6454 			} else {
6455 				// Create custom map
6456 				value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/));
6457 			}
6458 
6459 			return value;
6460 		}
6461 
6462 		settings = settings || {};
6463 		schemaItems = compileSchema(settings.schema);
6464 
6465 		// Allow all elements and attributes if verify_html is set to false
6466 		if (settings.verify_html === false) {
6467 			settings.valid_elements = '*[*]';
6468 		}
6469 
6470 		// Build styles list
6471 		if (settings.valid_styles) {
6472 			validStyles = {};
6473 
6474 			// Convert styles into a rule list
6475 			each(settings.valid_styles, function(value, key) {
6476 				validStyles[key] = explode(value);
6477 			});
6478 		}
6479 
6480 		// Setup map objects
6481 		whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
6482 		selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
6483 		shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
6484 			'meta param embed source wbr track');
6485 		boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
6486 			'noshade nowrap readonly selected autoplay loop controls');
6487 		nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
6488 		textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
6489 						'blockquote center dir fieldset header footer article section hgroup aside nav figure');
6490 		blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
6491 						'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
6492 						'datalist select optgroup', textBlockElementsMap);
6493 
6494 		each((settings.special || 'script noscript style textarea').split(' '), function(name) {
6495 			specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
6496 		});
6497 
6498 		// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
6499 		function patternToRegExp(str) {
6500 			return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
6501 		}
6502 
6503 		// Parses the specified valid_elements string and adds to the current rules
6504 		// This function is a bit hard to read since it's heavily optimized for speed
6505 		function addValidElements(valid_elements) {
6506 			var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
6507 				prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
6508 				elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
6509 				attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
6510 				hasPatternsRegExp = /[*?+]/;
6511 
6512 			if (valid_elements) {
6513 				// Split valid elements into an array with rules
6514 				valid_elements = split(valid_elements, ',');
6515 
6516 				if (elements['@']) {
6517 					globalAttributes = elements['@'].attributes;
6518 					globalAttributesOrder = elements['@'].attributesOrder;
6519 				}
6520 
6521 				// Loop all rules
6522 				for (ei = 0, el = valid_elements.length; ei < el; ei++) {
6523 					// Parse element rule
6524 					matches = elementRuleRegExp.exec(valid_elements[ei]);
6525 					if (matches) {
6526 						// Setup local names for matches
6527 						prefix = matches[1];
6528 						elementName = matches[2];
6529 						outputName = matches[3];
6530 						attrData = matches[5];
6531 
6532 						// Create new attributes and attributesOrder
6533 						attributes = {};
6534 						attributesOrder = [];
6535 
6536 						// Create the new element
6537 						element = {
6538 							attributes: attributes,
6539 							attributesOrder: attributesOrder
6540 						};
6541 
6542 						// Padd empty elements prefix
6543 						if (prefix === '#') {
6544 							element.paddEmpty = true;
6545 						}
6546 
6547 						// Remove empty elements prefix
6548 						if (prefix === '-') {
6549 							element.removeEmpty = true;
6550 						}
6551 
6552 						if (matches[4] === '!') {
6553 							element.removeEmptyAttrs = true;
6554 						}
6555 
6556 						// Copy attributes from global rule into current rule
6557 						if (globalAttributes) {
6558 							for (key in globalAttributes) {
6559 								attributes[key] = globalAttributes[key];
6560 							}
6561 
6562 							attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
6563 						}
6564 
6565 						// Attributes defined
6566 						if (attrData) {
6567 							attrData = split(attrData, '|');
6568 							for (ai = 0, al = attrData.length; ai < al; ai++) {
6569 								matches = attrRuleRegExp.exec(attrData[ai]);
6570 								if (matches) {
6571 									attr = {};
6572 									attrType = matches[1];
6573 									attrName = matches[2].replace(/::/g, ':');
6574 									prefix = matches[3];
6575 									value = matches[4];
6576 
6577 									// Required
6578 									if (attrType === '!') {
6579 										element.attributesRequired = element.attributesRequired || [];
6580 										element.attributesRequired.push(attrName);
6581 										attr.required = true;
6582 									}
6583 
6584 									// Denied from global
6585 									if (attrType === '-') {
6586 										delete attributes[attrName];
6587 										attributesOrder.splice(inArray(attributesOrder, attrName), 1);
6588 										continue;
6589 									}
6590 
6591 									// Default value
6592 									if (prefix) {
6593 										// Default value
6594 										if (prefix === '=') {
6595 											element.attributesDefault = element.attributesDefault || [];
6596 											element.attributesDefault.push({name: attrName, value: value});
6597 											attr.defaultValue = value;
6598 										}
6599 
6600 										// Forced value
6601 										if (prefix === ':') {
6602 											element.attributesForced = element.attributesForced || [];
6603 											element.attributesForced.push({name: attrName, value: value});
6604 											attr.forcedValue = value;
6605 										}
6606 
6607 										// Required values
6608 										if (prefix === '<') {
6609 											attr.validValues = makeMap(value, '?');
6610 										}
6611 									}
6612 
6613 									// Check for attribute patterns
6614 									if (hasPatternsRegExp.test(attrName)) {
6615 										element.attributePatterns = element.attributePatterns || [];
6616 										attr.pattern = patternToRegExp(attrName);
6617 										element.attributePatterns.push(attr);
6618 									} else {
6619 										// Add attribute to order list if it doesn't already exist
6620 										if (!attributes[attrName]) {
6621 											attributesOrder.push(attrName);
6622 										}
6623 
6624 										attributes[attrName] = attr;
6625 									}
6626 								}
6627 							}
6628 						}
6629 
6630 						// Global rule, store away these for later usage
6631 						if (!globalAttributes && elementName == '@') {
6632 							globalAttributes = attributes;
6633 							globalAttributesOrder = attributesOrder;
6634 						}
6635 
6636 						// Handle substitute elements such as b/strong
6637 						if (outputName) {
6638 							element.outputName = elementName;
6639 							elements[outputName] = element;
6640 						}
6641 
6642 						// Add pattern or exact element
6643 						if (hasPatternsRegExp.test(elementName)) {
6644 							element.pattern = patternToRegExp(elementName);
6645 							patternElements.push(element);
6646 						} else {
6647 							elements[elementName] = element;
6648 						}
6649 					}
6650 				}
6651 			}
6652 		}
6653 
6654 		function setValidElements(valid_elements) {
6655 			elements = {};
6656 			patternElements = [];
6657 
6658 			addValidElements(valid_elements);
6659 
6660 			each(schemaItems, function(element, name) {
6661 				children[name] = element.children;
6662 			});
6663 		}
6664 
6665 		// Adds custom non HTML elements to the schema
6666 		function addCustomElements(custom_elements) {
6667 			var customElementRegExp = /^(~)?(.+)$/;
6668 
6669 			if (custom_elements) {
6670 				// Flush cached items since we are altering the default maps
6671 				mapCache.text_block_elements = mapCache.block_elements = null;
6672 
6673 				each(split(custom_elements, ','), function(rule) {
6674 					var matches = customElementRegExp.exec(rule),
6675 						inline = matches[1] === '~',
6676 						cloneName = inline ? 'span' : 'div',
6677 						name = matches[2];
6678 
6679 					children[name] = children[cloneName];
6680 					customElementsMap[name] = cloneName;
6681 
6682 					// If it's not marked as inline then add it to valid block elements
6683 					if (!inline) {
6684 						blockElementsMap[name.toUpperCase()] = {};
6685 						blockElementsMap[name] = {};
6686 					}
6687 
6688 					// Add elements clone if needed
6689 					if (!elements[name]) {
6690 						var customRule = elements[cloneName];
6691 
6692 						customRule = extend({}, customRule);
6693 						delete customRule.removeEmptyAttrs;
6694 						delete customRule.removeEmpty;
6695 
6696 						elements[name] = customRule;
6697 					}
6698 
6699 					// Add custom elements at span/div positions
6700 					each(children, function(element, elmName) {
6701 						if (element[cloneName]) {
6702 							children[elmName] = element = extend({}, children[elmName]);
6703 							element[name] = element[cloneName];
6704 						}
6705 					});
6706 				});
6707 			}
6708 		}
6709 
6710 		// Adds valid children to the schema object
6711 		function addValidChildren(valid_children) {
6712 			var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
6713 
6714 			if (valid_children) {
6715 				each(split(valid_children, ','), function(rule) {
6716 					var matches = childRuleRegExp.exec(rule), parent, prefix;
6717 
6718 					if (matches) {
6719 						prefix = matches[1];
6720 
6721 						// Add/remove items from default
6722 						if (prefix) {
6723 							parent = children[matches[2]];
6724 						} else {
6725 							parent = children[matches[2]] = {'#comment': {}};
6726 						}
6727 
6728 						parent = children[matches[2]];
6729 
6730 						each(split(matches[3], '|'), function(child) {
6731 							if (prefix === '-') {
6732 								// Clone the element before we delete
6733 								// things in it to not mess up default schemas
6734 								children[matches[2]] = parent = extend({}, children[matches[2]]);
6735 
6736 								delete parent[child];
6737 							} else {
6738 								parent[child] = {};
6739 							}
6740 						});
6741 					}
6742 				});
6743 			}
6744 		}
6745 
6746 		function getElementRule(name) {
6747 			var element = elements[name], i;
6748 
6749 			// Exact match found
6750 			if (element) {
6751 				return element;
6752 			}
6753 
6754 			// No exact match then try the patterns
6755 			i = patternElements.length;
6756 			while (i--) {
6757 				element = patternElements[i];
6758 
6759 				if (element.pattern.test(name)) {
6760 					return element;
6761 				}
6762 			}
6763 		}
6764 
6765 		if (!settings.valid_elements) {
6766 			// No valid elements defined then clone the elements from the schema spec
6767 			each(schemaItems, function(element, name) {
6768 				elements[name] = {
6769 					attributes: element.attributes,
6770 					attributesOrder: element.attributesOrder
6771 				};
6772 
6773 				children[name] = element.children;
6774 			});
6775 
6776 			// Switch these on HTML4
6777 			if (settings.schema != "html5") {
6778 				each(split('strong/b em/i'), function(item) {
6779 					item = split(item, '/');
6780 					elements[item[1]].outputName = item[0];
6781 				});
6782 			}
6783 
6784 			// Add default alt attribute for images
6785 			elements.img.attributesDefault = [{name: 'alt', value: ''}];
6786 
6787 			// Remove these if they are empty by default
6788 			each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
6789 				if (elements[name]) {
6790 					elements[name].removeEmpty = true;
6791 				}
6792 			});
6793 
6794 			// Padd these by default
6795 			each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
6796 				elements[name].paddEmpty = true;
6797 			});
6798 
6799 			// Remove these if they have no attributes
6800 			each(split('span'), function(name) {
6801 				elements[name].removeEmptyAttrs = true;
6802 			});
6803 
6804 			// Remove these by default
6805 			// TODO: Reenable in 4.1
6806 			/*each(split('script style'), function(name) {
6807 				delete elements[name];
6808 			});*/
6809 		} else {
6810 			setValidElements(settings.valid_elements);
6811 		}
6812 
6813 		addCustomElements(settings.custom_elements);
6814 		addValidChildren(settings.valid_children);
6815 		addValidElements(settings.extended_valid_elements);
6816 
6817 		// Todo: Remove this when we fix list handling to be valid
6818 		addValidChildren('+ol[ul|ol],+ul[ul|ol]');
6819 
6820 		// Delete invalid elements
6821 		if (settings.invalid_elements) {
6822 			each(explode(settings.invalid_elements), function(item) {
6823 				if (elements[item]) {
6824 					delete elements[item];
6825 				}
6826 			});
6827 		}
6828 
6829 		// If the user didn't allow span only allow internal spans
6830 		if (!getElementRule('span')) {
6831 			addValidElements('span[!data-mce-type|*]');
6832 		}
6833 
6834 		/**
6835 		 * Name/value map object with valid parents and children to those parents.
6836 		 *
6837 		 * @example
6838 		 * children = {
6839 		 *    div:{p:{}, h1:{}}
6840 		 * };
6841 		 * @field children
6842 		 * @type Object
6843 		 */
6844 		self.children = children;
6845 
6846 		/**
6847 		 * Name/value map object with valid styles for each element.
6848 		 *
6849 		 * @field styles
6850 		 * @type Object
6851 		 */
6852 		self.styles = validStyles;
6853 
6854 		/**
6855 		 * Returns a map with boolean attributes.
6856 		 *
6857 		 * @method getBoolAttrs
6858 		 * @return {Object} Name/value lookup map for boolean attributes.
6859 		 */
6860 		self.getBoolAttrs = function() {
6861 			return boolAttrMap;
6862 		};
6863 
6864 		/**
6865 		 * Returns a map with block elements.
6866 		 *
6867 		 * @method getBlockElements
6868 		 * @return {Object} Name/value lookup map for block elements.
6869 		 */
6870 		self.getBlockElements = function() {
6871 			return blockElementsMap;
6872 		};
6873 
6874 		/**
6875 		 * Returns a map with text block elements. Such as: p,h1-h6,div,address
6876 		 *
6877 		 * @method getTextBlockElements
6878 		 * @return {Object} Name/value lookup map for block elements.
6879 		 */
6880 		self.getTextBlockElements = function() {
6881 			return textBlockElementsMap;
6882 		};
6883 
6884 		/**
6885 		 * Returns a map with short ended elements such as BR or IMG.
6886 		 *
6887 		 * @method getShortEndedElements
6888 		 * @return {Object} Name/value lookup map for short ended elements.
6889 		 */
6890 		self.getShortEndedElements = function() {
6891 			return shortEndedElementsMap;
6892 		};
6893 
6894 		/**
6895 		 * Returns a map with self closing tags such as <li>.
6896 		 *
6897 		 * @method getSelfClosingElements
6898 		 * @return {Object} Name/value lookup map for self closing tags elements.
6899 		 */
6900 		self.getSelfClosingElements = function() {
6901 			return selfClosingElementsMap;
6902 		};
6903 
6904 		/**
6905 		 * Returns a map with elements that should be treated as contents regardless if it has text
6906 		 * content in them or not such as TD, VIDEO or IMG.
6907 		 *
6908 		 * @method getNonEmptyElements
6909 		 * @return {Object} Name/value lookup map for non empty elements.
6910 		 */
6911 		self.getNonEmptyElements = function() {
6912 			return nonEmptyElementsMap;
6913 		};
6914 
6915 		/**
6916 		 * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
6917 		 *
6918 		 * @method getWhiteSpaceElements
6919 		 * @return {Object} Name/value lookup map for white space elements.
6920 		 */
6921 		self.getWhiteSpaceElements = function() {
6922 			return whiteSpaceElementsMap;
6923 		};
6924 
6925 		/**
6926 		 * Returns a map with special elements. These are elements that needs to be parsed
6927 		 * in a special way such as script, style, textarea etc. The map object values
6928 		 * are regexps used to find the end of the element.
6929 		 *
6930 		 * @method getSpecialElements
6931 		 * @return {Object} Name/value lookup map for special elements.
6932 		 */
6933 		self.getSpecialElements = function() {
6934 			return specialElements;
6935 		};
6936 
6937 		/**
6938 		 * Returns true/false if the specified element and it's child is valid or not
6939 		 * according to the schema.
6940 		 *
6941 		 * @method isValidChild
6942 		 * @param {String} name Element name to check for.
6943 		 * @param {String} child Element child to verify.
6944 		 * @return {Boolean} True/false if the element is a valid child of the specified parent.
6945 		 */
6946 		self.isValidChild = function(name, child) {
6947 			var parent = children[name];
6948 
6949 			return !!(parent && parent[child]);
6950 		};
6951 
6952 		/**
6953 		 * Returns true/false if the specified element name and optional attribute is
6954 		 * valid according to the schema.
6955 		 *
6956 		 * @method isValid
6957 		 * @param {String} name Name of element to check.
6958 		 * @param {String} attr Optional attribute name to check for.
6959 		 * @return {Boolean} True/false if the element and attribute is valid.
6960 		 */
6961 		self.isValid = function(name, attr) {
6962 			var attrPatterns, i, rule = getElementRule(name);
6963 
6964 			// Check if it's a valid element
6965 			if (rule) {
6966 				if (attr) {
6967 					// Check if attribute name exists
6968 					if (rule.attributes[attr]) {
6969 						return true;
6970 					}
6971 
6972 					// Check if attribute matches a regexp pattern
6973 					attrPatterns = rule.attributePatterns;
6974 					if (attrPatterns) {
6975 						i = attrPatterns.length;
6976 						while (i--) {
6977 							if (attrPatterns[i].pattern.test(name)) {
6978 								return true;
6979 							}
6980 						}
6981 					}
6982 				} else {
6983 					return true;
6984 				}
6985 			}
6986 
6987 			// No match
6988 			return false;
6989 		};
6990 
6991 		/**
6992 		 * Returns true/false if the specified element is valid or not
6993 		 * according to the schema.
6994 		 *
6995 		 * @method getElementRule
6996 		 * @param {String} name Element name to check for.
6997 		 * @return {Object} Element object or undefined if the element isn't valid.
6998 		 */
6999 		self.getElementRule = getElementRule;
7000 
7001 		/**
7002 		 * Returns an map object of all custom elements.
7003 		 *
7004 		 * @method getCustomElements
7005 		 * @return {Object} Name/value map object of all custom elements.
7006 		 */
7007 		self.getCustomElements = function() {
7008 			return customElementsMap;
7009 		};
7010 
7011 		/**
7012 		 * Parses a valid elements string and adds it to the schema. The valid elements
7013 		 * format is for example "element[attr=default|otherattr]".
7014 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
7015 		 *
7016 		 * @method addValidElements
7017 		 * @param {String} valid_elements String in the valid elements format to be parsed.
7018 		 */
7019 		self.addValidElements = addValidElements;
7020 
7021 		/**
7022 		 * Parses a valid elements string and sets it to the schema. The valid elements
7023 		 * format is for example "element[attr=default|otherattr]".
7024 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
7025 		 *
7026 		 * @method setValidElements
7027 		 * @param {String} valid_elements String in the valid elements format to be parsed.
7028 		 */
7029 		self.setValidElements = setValidElements;
7030 
7031 		/**
7032 		 * Adds custom non HTML elements to the schema.
7033 		 *
7034 		 * @method addCustomElements
7035 		 * @param {String} custom_elements Comma separated list of custom elements to add.
7036 		 */
7037 		self.addCustomElements = addCustomElements;
7038 
7039 		/**
7040 		 * Parses a valid children string and adds them to the schema structure. The valid children
7041 		 * format is for example: "element[child1|child2]".
7042 		 *
7043 		 * @method addValidChildren
7044 		 * @param {String} valid_children Valid children elements string to parse
7045 		 */
7046 		self.addValidChildren = addValidChildren;
7047 
7048 		self.elements = elements;
7049 	};
7050 });
7051 
7052 // Included from: js/tinymce/classes/html/SaxParser.js
7053 
7054 /**
7055  * SaxParser.js
7056  *
7057  * Copyright, Moxiecode Systems AB
7058  * Released under LGPL License.
7059  *
7060  * License: http://www.tinymce.com/license
7061  * Contributing: http://www.tinymce.com/contributing
7062  */
7063 
7064 /*eslint max-depth:[2, 9] */
7065 
7066 /**
7067  * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
7068  * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
7069  * and attributes that doesn't fit the schema if the validate setting is enabled.
7070  *
7071  * @example
7072  * var parser = new tinymce.html.SaxParser({
7073  *     validate: true,
7074  *
7075  *     comment: function(text) {
7076  *         console.log('Comment:', text);
7077  *     },
7078  *
7079  *     cdata: function(text) {
7080  *         console.log('CDATA:', text);
7081  *     },
7082  *
7083  *     text: function(text, raw) {
7084  *         console.log('Text:', text, 'Raw:', raw);
7085  *     },
7086  *
7087  *     start: function(name, attrs, empty) {
7088  *         console.log('Start:', name, attrs, empty);
7089  *     },
7090  *
7091  *     end: function(name) {
7092  *         console.log('End:', name);
7093  *     },
7094  *
7095  *     pi: function(name, text) {
7096  *         console.log('PI:', name, text);
7097  *     },
7098  *
7099  *     doctype: function(text) {
7100  *         console.log('DocType:', text);
7101  *     }
7102  * }, schema);
7103  * @class tinymce.html.SaxParser
7104  * @version 3.4
7105  */
7106 define("tinymce/html/SaxParser", [
7107 	"tinymce/html/Schema",
7108 	"tinymce/html/Entities",
7109 	"tinymce/util/Tools"
7110 ], function(Schema, Entities, Tools) {
7111 	var each = Tools.each;
7112 
7113 	/**
7114 	 * Constructs a new SaxParser instance.
7115 	 *
7116 	 * @constructor
7117 	 * @method SaxParser
7118 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
7119 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
7120 	 */
7121 	return function(settings, schema) {
7122 		var self = this;
7123 
7124 		function noop() {}
7125 
7126 		settings = settings || {};
7127 		self.schema = schema = schema || new Schema();
7128 
7129 		if (settings.fix_self_closing !== false) {
7130 			settings.fix_self_closing = true;
7131 		}
7132 
7133 		// Add handler functions from settings and setup default handlers
7134 		each('comment cdata text start end pi doctype'.split(' '), function(name) {
7135 			if (name) {
7136 				self[name] = settings[name] || noop;
7137 			}
7138 		});
7139 
7140 		/**
7141 		 * Parses the specified HTML string and executes the callbacks for each item it finds.
7142 		 *
7143 		 * @example
7144 		 * new SaxParser({...}).parse('<b>text</b>');
7145 		 * @method parse
7146 		 * @param {String} html Html string to sax parse.
7147 		 */
7148 		self.parse = function(html) {
7149 			var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
7150 			var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
7151 			var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
7152 			var attributesRequired, attributesDefault, attributesForced;
7153 			var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
7154 			var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster');
7155 			var scriptUriRegExp = /((java|vb)script|mhtml):/i, dataUriRegExp = /^data:/i;
7156 
7157 			function processEndTag(name) {
7158 				var pos, i;
7159 
7160 				// Find position of parent of the same type
7161 				pos = stack.length;
7162 				while (pos--) {
7163 					if (stack[pos].name === name) {
7164 						break;
7165 					}
7166 				}
7167 
7168 				// Found parent
7169 				if (pos >= 0) {
7170 					// Close all the open elements
7171 					for (i = stack.length - 1; i >= pos; i--) {
7172 						name = stack[i];
7173 
7174 						if (name.valid) {
7175 							self.end(name.name);
7176 						}
7177 					}
7178 
7179 					// Remove the open elements from the stack
7180 					stack.length = pos;
7181 				}
7182 			}
7183 
7184 			function parseAttribute(match, name, value, val2, val3) {
7185 				var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
7186 
7187 				name = name.toLowerCase();
7188 				value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
7189 
7190 				// Validate name and value pass through all data- attributes
7191 				if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
7192 					attrRule = validAttributesMap[name];
7193 
7194 					// Find rule by pattern matching
7195 					if (!attrRule && validAttributePatterns) {
7196 						i = validAttributePatterns.length;
7197 						while (i--) {
7198 							attrRule = validAttributePatterns[i];
7199 							if (attrRule.pattern.test(name)) {
7200 								break;
7201 							}
7202 						}
7203 
7204 						// No rule matched
7205 						if (i === -1) {
7206 							attrRule = null;
7207 						}
7208 					}
7209 
7210 					// No attribute rule found
7211 					if (!attrRule) {
7212 						return;
7213 					}
7214 
7215 					// Validate value
7216 					if (attrRule.validValues && !(value in attrRule.validValues)) {
7217 						return;
7218 					}
7219 				}
7220 
7221 				// Block any javascript: urls or non image data uris
7222 				if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
7223 					var uri = value.replace(trimRegExp, '');
7224 
7225 					try {
7226 						// Might throw malformed URI sequence
7227 						uri = decodeURIComponent(uri);
7228 					} catch (ex) {
7229 						// Fallback to non UTF-8 decoder
7230 						uri = unescape(uri);
7231 					}
7232 
7233 					if (scriptUriRegExp.test(uri)) {
7234 						return;
7235 					}
7236 
7237 					if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) {
7238 						return;
7239 					}
7240 				}
7241 
7242 				// Add attribute to list and map
7243 				attrList.map[name] = value;
7244 				attrList.push({
7245 					name: name,
7246 					value: value
7247 				});
7248 			}
7249 
7250 			// Precompile RegExps and map objects
7251 			tokenRegExp = new RegExp('<(?:' +
7252 				'(?:!--([\\w\\W]*?)-->)|' + // Comment
7253 				'(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
7254 				'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
7255 				'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
7256 				'(?:\\/([^>]+)>)|' + // End element
7257 				'(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
7258 			')', 'g');
7259 
7260 			attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
7261 
7262 			// Setup lookup tables for empty elements and boolean attributes
7263 			shortEndedElements = schema.getShortEndedElements();
7264 			selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
7265 			fillAttrsMap = schema.getBoolAttrs();
7266 			validate = settings.validate;
7267 			removeInternalElements = settings.remove_internals;
7268 			fixSelfClosing = settings.fix_self_closing;
7269 			specialElements = schema.getSpecialElements();
7270 
7271 			while ((matches = tokenRegExp.exec(html))) {
7272 				// Text
7273 				if (index < matches.index) {
7274 					self.text(decode(html.substr(index, matches.index - index)));
7275 				}
7276 
7277 				if ((value = matches[6])) { // End element
7278 					value = value.toLowerCase();
7279 
7280 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
7281 					if (value.charAt(0) === ':') {
7282 						value = value.substr(1);
7283 					}
7284 
7285 					processEndTag(value);
7286 				} else if ((value = matches[7])) { // Start element
7287 					value = value.toLowerCase();
7288 
7289 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
7290 					if (value.charAt(0) === ':') {
7291 						value = value.substr(1);
7292 					}
7293 
7294 					isShortEnded = value in shortEndedElements;
7295 
7296 					// Is self closing tag for example an <li> after an open <li>
7297 					if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
7298 						processEndTag(value);
7299 					}
7300 
7301 					// Validate element
7302 					if (!validate || (elementRule = schema.getElementRule(value))) {
7303 						isValidElement = true;
7304 
7305 						// Grab attributes map and patters when validation is enabled
7306 						if (validate) {
7307 							validAttributesMap = elementRule.attributes;
7308 							validAttributePatterns = elementRule.attributePatterns;
7309 						}
7310 
7311 						// Parse attributes
7312 						if ((attribsValue = matches[8])) {
7313 							isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
7314 
7315 							// If the element has internal attributes then remove it if we are told to do so
7316 							if (isInternalElement && removeInternalElements) {
7317 								isValidElement = false;
7318 							}
7319 
7320 							attrList = [];
7321 							attrList.map = {};
7322 
7323 							attribsValue.replace(attrRegExp, parseAttribute);
7324 						} else {
7325 							attrList = [];
7326 							attrList.map = {};
7327 						}
7328 
7329 						// Process attributes if validation is enabled
7330 						if (validate && !isInternalElement) {
7331 							attributesRequired = elementRule.attributesRequired;
7332 							attributesDefault = elementRule.attributesDefault;
7333 							attributesForced = elementRule.attributesForced;
7334 							anyAttributesRequired = elementRule.removeEmptyAttrs;
7335 
7336 							// Check if any attribute exists
7337 							if (anyAttributesRequired && !attrList.length) {
7338 								isValidElement = false;
7339 							}
7340 
7341 							// Handle forced attributes
7342 							if (attributesForced) {
7343 								i = attributesForced.length;
7344 								while (i--) {
7345 									attr = attributesForced[i];
7346 									name = attr.name;
7347 									attrValue = attr.value;
7348 
7349 									if (attrValue === '{$uid}') {
7350 										attrValue = 'mce_' + idCount++;
7351 									}
7352 
7353 									attrList.map[name] = attrValue;
7354 									attrList.push({name: name, value: attrValue});
7355 								}
7356 							}
7357 
7358 							// Handle default attributes
7359 							if (attributesDefault) {
7360 								i = attributesDefault.length;
7361 								while (i--) {
7362 									attr = attributesDefault[i];
7363 									name = attr.name;
7364 
7365 									if (!(name in attrList.map)) {
7366 										attrValue = attr.value;
7367 
7368 										if (attrValue === '{$uid}') {
7369 											attrValue = 'mce_' + idCount++;
7370 										}
7371 
7372 										attrList.map[name] = attrValue;
7373 										attrList.push({name: name, value: attrValue});
7374 									}
7375 								}
7376 							}
7377 
7378 							// Handle required attributes
7379 							if (attributesRequired) {
7380 								i = attributesRequired.length;
7381 								while (i--) {
7382 									if (attributesRequired[i] in attrList.map) {
7383 										break;
7384 									}
7385 								}
7386 
7387 								// None of the required attributes where found
7388 								if (i === -1) {
7389 									isValidElement = false;
7390 								}
7391 							}
7392 
7393 							// Invalidate element if it's marked as bogus
7394 							if (attrList.map['data-mce-bogus']) {
7395 								isValidElement = false;
7396 							}
7397 						}
7398 
7399 						if (isValidElement) {
7400 							self.start(value, attrList, isShortEnded);
7401 						}
7402 					} else {
7403 						isValidElement = false;
7404 					}
7405 
7406 					// Treat script, noscript and style a bit different since they may include code that looks like elements
7407 					if ((endRegExp = specialElements[value])) {
7408 						endRegExp.lastIndex = index = matches.index + matches[0].length;
7409 
7410 						if ((matches = endRegExp.exec(html))) {
7411 							if (isValidElement) {
7412 								text = html.substr(index, matches.index - index);
7413 							}
7414 
7415 							index = matches.index + matches[0].length;
7416 						} else {
7417 							text = html.substr(index);
7418 							index = html.length;
7419 						}
7420 
7421 						if (isValidElement) {
7422 							if (text.length > 0) {
7423 								self.text(text, true);
7424 							}
7425 
7426 							self.end(value);
7427 						}
7428 
7429 						tokenRegExp.lastIndex = index;
7430 						continue;
7431 					}
7432 
7433 					// Push value on to stack
7434 					if (!isShortEnded) {
7435 						if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
7436 							stack.push({name: value, valid: isValidElement});
7437 						} else if (isValidElement) {
7438 							self.end(value);
7439 						}
7440 					}
7441 				} else if ((value = matches[1])) { // Comment
7442 					// Padd comment value to avoid browsers from parsing invalid comments as HTML
7443 					if (value.charAt(0) === '>') {
7444 						value = ' ' + value;
7445 					}
7446 
7447 					if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
7448 						value = ' ' + value;
7449 					}
7450 
7451 					self.comment(value);
7452 				} else if ((value = matches[2])) { // CDATA
7453 					self.cdata(value);
7454 				} else if ((value = matches[3])) { // DOCTYPE
7455 					self.doctype(value);
7456 				} else if ((value = matches[4])) { // PI
7457 					self.pi(value, matches[5]);
7458 				}
7459 
7460 				index = matches.index + matches[0].length;
7461 			}
7462 
7463 			// Text
7464 			if (index < html.length) {
7465 				self.text(decode(html.substr(index)));
7466 			}
7467 
7468 			// Close any open elements
7469 			for (i = stack.length - 1; i >= 0; i--) {
7470 				value = stack[i];
7471 
7472 				if (value.valid) {
7473 					self.end(value.name);
7474 				}
7475 			}
7476 		};
7477 	};
7478 });
7479 
7480 // Included from: js/tinymce/classes/html/DomParser.js
7481 
7482 /**
7483  * DomParser.js
7484  *
7485  * Copyright, Moxiecode Systems AB
7486  * Released under LGPL License.
7487  *
7488  * License: http://www.tinymce.com/license
7489  * Contributing: http://www.tinymce.com/contributing
7490  */
7491 
7492 /**
7493  * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
7494  * sure that the node tree is valid according to the specified schema.
7495  * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
7496  *
7497  * @example
7498  * var parser = new tinymce.html.DomParser({validate: true}, schema);
7499  * var rootNode = parser.parse('<h1>content</h1>');
7500  *
7501  * @class tinymce.html.DomParser
7502  * @version 3.4
7503  */
7504 define("tinymce/html/DomParser", [
7505 	"tinymce/html/Node",
7506 	"tinymce/html/Schema",
7507 	"tinymce/html/SaxParser",
7508 	"tinymce/util/Tools"
7509 ], function(Node, Schema, SaxParser, Tools) {
7510 	var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
7511 
7512 	/**
7513 	 * Constructs a new DomParser instance.
7514 	 *
7515 	 * @constructor
7516 	 * @method DomParser
7517 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
7518 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
7519 	 */
7520 	return function(settings, schema) {
7521 		var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
7522 
7523 		settings = settings || {};
7524 		settings.validate = "validate" in settings ? settings.validate : true;
7525 		settings.root_name = settings.root_name || 'body';
7526 		self.schema = schema = schema || new Schema();
7527 
7528 		function fixInvalidChildren(nodes) {
7529 			var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
7530 			var nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
7531 
7532 			nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
7533 			nonEmptyElements = schema.getNonEmptyElements();
7534 			textBlockElements = schema.getTextBlockElements();
7535 
7536 			for (ni = 0; ni < nodes.length; ni++) {
7537 				node = nodes[ni];
7538 
7539 				// Already removed or fixed
7540 				if (!node.parent || node.fixed) {
7541 					continue;
7542 				}
7543 
7544 				// If the invalid element is a text block and the text block is within a parent LI element
7545 				// Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
7546 				if (textBlockElements[node.name] && node.parent.name == 'li') {
7547 					// Move sibling text blocks after LI element
7548 					sibling = node.next;
7549 					while (sibling) {
7550 						if (textBlockElements[sibling.name]) {
7551 							sibling.name = 'li';
7552 							sibling.fixed = true;
7553 							node.parent.insert(sibling, node.parent);
7554 						} else {
7555 							break;
7556 						}
7557 
7558 						sibling = sibling.next;
7559 					}
7560 
7561 					// Unwrap current text block
7562 					node.unwrap(node);
7563 					continue;
7564 				}
7565 
7566 				// Get list of all parent nodes until we find a valid parent to stick the child into
7567 				parents = [node];
7568 				for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
7569 					!nonSplitableElements[parent.name]; parent = parent.parent) {
7570 					parents.push(parent);
7571 				}
7572 
7573 				// Found a suitable parent
7574 				if (parent && parents.length > 1) {
7575 					// Reverse the array since it makes looping easier
7576 					parents.reverse();
7577 
7578 					// Clone the related parent and insert that after the moved node
7579 					newParent = currentNode = self.filterNode(parents[0].clone());
7580 
7581 					// Start cloning and moving children on the left side of the target node
7582 					for (i = 0; i < parents.length - 1; i++) {
7583 						if (schema.isValidChild(currentNode.name, parents[i].name)) {
7584 							tempNode = self.filterNode(parents[i].clone());
7585 							currentNode.append(tempNode);
7586 						} else {
7587 							tempNode = currentNode;
7588 						}
7589 
7590 						for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {
7591 							nextNode = childNode.next;
7592 							tempNode.append(childNode);
7593 							childNode = nextNode;
7594 						}
7595 
7596 						currentNode = tempNode;
7597 					}
7598 
7599 					if (!newParent.isEmpty(nonEmptyElements)) {
7600 						parent.insert(newParent, parents[0], true);
7601 						parent.insert(node, newParent);
7602 					} else {
7603 						parent.insert(node, parents[0], true);
7604 					}
7605 
7606 					// Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
7607 					parent = parents[0];
7608 					if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
7609 						parent.empty().remove();
7610 					}
7611 				} else if (node.parent) {
7612 					// If it's an LI try to find a UL/OL for it or wrap it
7613 					if (node.name === 'li') {
7614 						sibling = node.prev;
7615 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
7616 							sibling.append(node);
7617 							continue;
7618 						}
7619 
7620 						sibling = node.next;
7621 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
7622 							sibling.insert(node, sibling.firstChild, true);
7623 							continue;
7624 						}
7625 
7626 						node.wrap(self.filterNode(new Node('ul', 1)));
7627 						continue;
7628 					}
7629 
7630 					// Try wrapping the element in a DIV
7631 					if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
7632 						node.wrap(self.filterNode(new Node('div', 1)));
7633 					} else {
7634 						// We failed wrapping it, then remove or unwrap it
7635 						if (node.name === 'style' || node.name === 'script') {
7636 							node.empty().remove();
7637 						} else {
7638 							node.unwrap();
7639 						}
7640 					}
7641 				}
7642 			}
7643 		}
7644 
7645 		/**
7646 		 * Runs the specified node though the element and attributes filters.
7647 		 *
7648 		 * @method filterNode
7649 		 * @param {tinymce.html.Node} Node the node to run filters on.
7650 		 * @return {tinymce.html.Node} The passed in node.
7651 		 */
7652 		self.filterNode = function(node) {
7653 			var i, name, list;
7654 
7655 			// Run element filters
7656 			if (name in nodeFilters) {
7657 				list = matchedNodes[name];
7658 
7659 				if (list) {
7660 					list.push(node);
7661 				} else {
7662 					matchedNodes[name] = [node];
7663 				}
7664 			}
7665 
7666 			// Run attribute filters
7667 			i = attributeFilters.length;
7668 			while (i--) {
7669 				name = attributeFilters[i].name;
7670 
7671 				if (name in node.attributes.map) {
7672 					list = matchedAttributes[name];
7673 
7674 					if (list) {
7675 						list.push(node);
7676 					} else {
7677 						matchedAttributes[name] = [node];
7678 					}
7679 				}
7680 			}
7681 
7682 			return node;
7683 		};
7684 
7685 		/**
7686 		 * Adds a node filter function to the parser, the parser will collect the specified nodes by name
7687 		 * and then execute the callback ones it has finished parsing the document.
7688 		 *
7689 		 * @example
7690 		 * parser.addNodeFilter('p,h1', function(nodes, name) {
7691 		 *		for (var i = 0; i < nodes.length; i++) {
7692 		 *			console.log(nodes[i].name);
7693 		 *		}
7694 		 * });
7695 		 * @method addNodeFilter
7696 		 * @method {String} name Comma separated list of nodes to collect.
7697 		 * @param {function} callback Callback function to execute once it has collected nodes.
7698 		 */
7699 		self.addNodeFilter = function(name, callback) {
7700 			each(explode(name), function(name) {
7701 				var list = nodeFilters[name];
7702 
7703 				if (!list) {
7704 					nodeFilters[name] = list = [];
7705 				}
7706 
7707 				list.push(callback);
7708 			});
7709 		};
7710 
7711 		/**
7712 		 * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
7713 		 * and then execute the callback ones it has finished parsing the document.
7714 		 *
7715 		 * @example
7716 		 * parser.addAttributeFilter('src,href', function(nodes, name) {
7717 		 *		for (var i = 0; i < nodes.length; i++) {
7718 		 *			console.log(nodes[i].name);
7719 		 *		}
7720 		 * });
7721 		 * @method addAttributeFilter
7722 		 * @method {String} name Comma separated list of nodes to collect.
7723 		 * @param {function} callback Callback function to execute once it has collected nodes.
7724 		 */
7725 		self.addAttributeFilter = function(name, callback) {
7726 			each(explode(name), function(name) {
7727 				var i;
7728 
7729 				for (i = 0; i < attributeFilters.length; i++) {
7730 					if (attributeFilters[i].name === name) {
7731 						attributeFilters[i].callbacks.push(callback);
7732 						return;
7733 					}
7734 				}
7735 
7736 				attributeFilters.push({name: name, callbacks: [callback]});
7737 			});
7738 		};
7739 
7740 		/**
7741 		 * Parses the specified HTML string into a DOM like node tree and returns the result.
7742 		 *
7743 		 * @example
7744 		 * var rootNode = new DomParser({...}).parse('<b>text</b>');
7745 		 * @method parse
7746 		 * @param {String} html Html string to sax parse.
7747 		 * @param {Object} args Optional args object that gets passed to all filter functions.
7748 		 * @return {tinymce.html.Node} Root node containing the tree.
7749 		 */
7750 		self.parse = function(html, args) {
7751 			var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
7752 			var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
7753 			var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
7754 			var children, nonEmptyElements, rootBlockName;
7755 
7756 			args = args || {};
7757 			matchedNodes = {};
7758 			matchedAttributes = {};
7759 			blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
7760 			nonEmptyElements = schema.getNonEmptyElements();
7761 			children = schema.children;
7762 			validate = settings.validate;
7763 			rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
7764 
7765 			whiteSpaceElements = schema.getWhiteSpaceElements();
7766 			startWhiteSpaceRegExp = /^[ \t\r\n]+/;
7767 			endWhiteSpaceRegExp = /[ \t\r\n]+$/;
7768 			allWhiteSpaceRegExp = /[ \t\r\n]+/g;
7769 			isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
7770 
7771 			function addRootBlocks() {
7772 				var node = rootNode.firstChild, next, rootBlockNode;
7773 
7774 				// Removes whitespace at beginning and end of block so:
7775 				// <p> x </p> -> <p>x</p>
7776 				function trim(rootBlockNode) {
7777 					if (rootBlockNode) {
7778 						node = rootBlockNode.firstChild;
7779 						if (node && node.type == 3) {
7780 							node.value = node.value.replace(startWhiteSpaceRegExp, '');
7781 						}
7782 
7783 						node = rootBlockNode.lastChild;
7784 						if (node && node.type == 3) {
7785 							node.value = node.value.replace(endWhiteSpaceRegExp, '');
7786 						}
7787 					}
7788 				}
7789 
7790 				// Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
7791 				if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
7792 					return;
7793 				}
7794 
7795 				while (node) {
7796 					next = node.next;
7797 
7798 					if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
7799 						!blockElements[node.name] && !node.attr('data-mce-type'))) {
7800 						if (!rootBlockNode) {
7801 							// Create a new root block element
7802 							rootBlockNode = createNode(rootBlockName, 1);
7803 							rootBlockNode.attr(settings.forced_root_block_attrs);
7804 							rootNode.insert(rootBlockNode, node);
7805 							rootBlockNode.append(node);
7806 						} else {
7807 							rootBlockNode.append(node);
7808 						}
7809 					} else {
7810 						trim(rootBlockNode);
7811 						rootBlockNode = null;
7812 					}
7813 
7814 					node = next;
7815 				}
7816 
7817 				trim(rootBlockNode);
7818 			}
7819 
7820 			function createNode(name, type) {
7821 				var node = new Node(name, type), list;
7822 
7823 				if (name in nodeFilters) {
7824 					list = matchedNodes[name];
7825 
7826 					if (list) {
7827 						list.push(node);
7828 					} else {
7829 						matchedNodes[name] = [node];
7830 					}
7831 				}
7832 
7833 				return node;
7834 			}
7835 
7836 			function removeWhitespaceBefore(node) {
7837 				var textNode, textVal, sibling;
7838 
7839 				for (textNode = node.prev; textNode && textNode.type === 3; ) {
7840 					textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
7841 
7842 					if (textVal.length > 0) {
7843 						textNode.value = textVal;
7844 						textNode = textNode.prev;
7845 					} else {
7846 						sibling = textNode.prev;
7847 						textNode.remove();
7848 						textNode = sibling;
7849 					}
7850 				}
7851 			}
7852 
7853 			function cloneAndExcludeBlocks(input) {
7854 				var name, output = {};
7855 
7856 				for (name in input) {
7857 					if (name !== 'li' && name != 'p') {
7858 						output[name] = input[name];
7859 					}
7860 				}
7861 
7862 				return output;
7863 			}
7864 
7865 			parser = new SaxParser({
7866 				validate: validate,
7867 				allow_script_urls: settings.allow_script_urls,
7868 				allow_conditional_comments: settings.allow_conditional_comments,
7869 
7870 				// Exclude P and LI from DOM parsing since it's treated better by the DOM parser
7871 				self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
7872 
7873 				cdata: function(text) {
7874 					node.append(createNode('#cdata', 4)).value = text;
7875 				},
7876 
7877 				text: function(text, raw) {
7878 					var textNode;
7879 
7880 					// Trim all redundant whitespace on non white space elements
7881 					if (!isInWhiteSpacePreservedElement) {
7882 						text = text.replace(allWhiteSpaceRegExp, ' ');
7883 
7884 						if (node.lastChild && blockElements[node.lastChild.name]) {
7885 							text = text.replace(startWhiteSpaceRegExp, '');
7886 						}
7887 					}
7888 
7889 					// Do we need to create the node
7890 					if (text.length !== 0) {
7891 						textNode = createNode('#text', 3);
7892 						textNode.raw = !!raw;
7893 						node.append(textNode).value = text;
7894 					}
7895 				},
7896 
7897 				comment: function(text) {
7898 					node.append(createNode('#comment', 8)).value = text;
7899 				},
7900 
7901 				pi: function(name, text) {
7902 					node.append(createNode(name, 7)).value = text;
7903 					removeWhitespaceBefore(node);
7904 				},
7905 
7906 				doctype: function(text) {
7907 					var newNode;
7908 
7909 					newNode = node.append(createNode('#doctype', 10));
7910 					newNode.value = text;
7911 					removeWhitespaceBefore(node);
7912 				},
7913 
7914 				start: function(name, attrs, empty) {
7915 					var newNode, attrFiltersLen, elementRule, attrName, parent;
7916 
7917 					elementRule = validate ? schema.getElementRule(name) : {};
7918 					if (elementRule) {
7919 						newNode = createNode(elementRule.outputName || name, 1);
7920 						newNode.attributes = attrs;
7921 						newNode.shortEnded = empty;
7922 
7923 						node.append(newNode);
7924 
7925 						// Check if node is valid child of the parent node is the child is
7926 						// unknown we don't collect it since it's probably a custom element
7927 						parent = children[node.name];
7928 						if (parent && children[newNode.name] && !parent[newNode.name]) {
7929 							invalidChildren.push(newNode);
7930 						}
7931 
7932 						attrFiltersLen = attributeFilters.length;
7933 						while (attrFiltersLen--) {
7934 							attrName = attributeFilters[attrFiltersLen].name;
7935 
7936 							if (attrName in attrs.map) {
7937 								list = matchedAttributes[attrName];
7938 
7939 								if (list) {
7940 									list.push(newNode);
7941 								} else {
7942 									matchedAttributes[attrName] = [newNode];
7943 								}
7944 							}
7945 						}
7946 
7947 						// Trim whitespace before block
7948 						if (blockElements[name]) {
7949 							removeWhitespaceBefore(newNode);
7950 						}
7951 
7952 						// Change current node if the element wasn't empty i.e not <br /> or <img />
7953 						if (!empty) {
7954 							node = newNode;
7955 						}
7956 
7957 						// Check if we are inside a whitespace preserved element
7958 						if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
7959 							isInWhiteSpacePreservedElement = true;
7960 						}
7961 					}
7962 				},
7963 
7964 				end: function(name) {
7965 					var textNode, elementRule, text, sibling, tempNode;
7966 
7967 					elementRule = validate ? schema.getElementRule(name) : {};
7968 					if (elementRule) {
7969 						if (blockElements[name]) {
7970 							if (!isInWhiteSpacePreservedElement) {
7971 								// Trim whitespace of the first node in a block
7972 								textNode = node.firstChild;
7973 								if (textNode && textNode.type === 3) {
7974 									text = textNode.value.replace(startWhiteSpaceRegExp, '');
7975 
7976 									// Any characters left after trim or should we remove it
7977 									if (text.length > 0) {
7978 										textNode.value = text;
7979 										textNode = textNode.next;
7980 									} else {
7981 										sibling = textNode.next;
7982 										textNode.remove();
7983 										textNode = sibling;
7984 
7985 										// Remove any pure whitespace siblings
7986 										while (textNode && textNode.type === 3) {
7987 											text = textNode.value;
7988 											sibling = textNode.next;
7989 
7990 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
7991 												textNode.remove();
7992 												textNode = sibling;
7993 											}
7994 
7995 											textNode = sibling;
7996 										}
7997 									}
7998 								}
7999 
8000 								// Trim whitespace of the last node in a block
8001 								textNode = node.lastChild;
8002 								if (textNode && textNode.type === 3) {
8003 									text = textNode.value.replace(endWhiteSpaceRegExp, '');
8004 
8005 									// Any characters left after trim or should we remove it
8006 									if (text.length > 0) {
8007 										textNode.value = text;
8008 										textNode = textNode.prev;
8009 									} else {
8010 										sibling = textNode.prev;
8011 										textNode.remove();
8012 										textNode = sibling;
8013 
8014 										// Remove any pure whitespace siblings
8015 										while (textNode && textNode.type === 3) {
8016 											text = textNode.value;
8017 											sibling = textNode.prev;
8018 
8019 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
8020 												textNode.remove();
8021 												textNode = sibling;
8022 											}
8023 
8024 											textNode = sibling;
8025 										}
8026 									}
8027 								}
8028 							}
8029 
8030 							// Trim start white space
8031 							// Removed due to: #5424
8032 							/*textNode = node.prev;
8033 							if (textNode && textNode.type === 3) {
8034 								text = textNode.value.replace(startWhiteSpaceRegExp, '');
8035 
8036 								if (text.length > 0)
8037 									textNode.value = text;
8038 								else
8039 									textNode.remove();
8040 							}*/
8041 						}
8042 
8043 						// Check if we exited a whitespace preserved element
8044 						if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
8045 							isInWhiteSpacePreservedElement = false;
8046 						}
8047 
8048 						// Handle empty nodes
8049 						if (elementRule.removeEmpty || elementRule.paddEmpty) {
8050 							if (node.isEmpty(nonEmptyElements)) {
8051 								if (elementRule.paddEmpty) {
8052 									node.empty().append(new Node('#text', '3')).value = '\u00a0';
8053 								} else {
8054 									// Leave nodes that have a name like <a name="name">
8055 									if (!node.attributes.map.name && !node.attributes.map.id) {
8056 										tempNode = node.parent;
8057 										node.empty().remove();
8058 										node = tempNode;
8059 										return;
8060 									}
8061 								}
8062 							}
8063 						}
8064 
8065 						node = node.parent;
8066 					}
8067 				}
8068 			}, schema);
8069 
8070 			rootNode = node = new Node(args.context || settings.root_name, 11);
8071 
8072 			parser.parse(html);
8073 
8074 			// Fix invalid children or report invalid children in a contextual parsing
8075 			if (validate && invalidChildren.length) {
8076 				if (!args.context) {
8077 					fixInvalidChildren(invalidChildren);
8078 				} else {
8079 					args.invalid = true;
8080 				}
8081 			}
8082 
8083 			// Wrap nodes in the root into block elements if the root is body
8084 			if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
8085 				addRootBlocks();
8086 			}
8087 
8088 			// Run filters only when the contents is valid
8089 			if (!args.invalid) {
8090 				// Run node filters
8091 				for (name in matchedNodes) {
8092 					list = nodeFilters[name];
8093 					nodes = matchedNodes[name];
8094 
8095 					// Remove already removed children
8096 					fi = nodes.length;
8097 					while (fi--) {
8098 						if (!nodes[fi].parent) {
8099 							nodes.splice(fi, 1);
8100 						}
8101 					}
8102 
8103 					for (i = 0, l = list.length; i < l; i++) {
8104 						list[i](nodes, name, args);
8105 					}
8106 				}
8107 
8108 				// Run attribute filters
8109 				for (i = 0, l = attributeFilters.length; i < l; i++) {
8110 					list = attributeFilters[i];
8111 
8112 					if (list.name in matchedAttributes) {
8113 						nodes = matchedAttributes[list.name];
8114 
8115 						// Remove already removed children
8116 						fi = nodes.length;
8117 						while (fi--) {
8118 							if (!nodes[fi].parent) {
8119 								nodes.splice(fi, 1);
8120 							}
8121 						}
8122 
8123 						for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
8124 							list.callbacks[fi](nodes, list.name, args);
8125 						}
8126 					}
8127 				}
8128 			}
8129 
8130 			return rootNode;
8131 		};
8132 
8133 		// Remove <br> at end of block elements Gecko and WebKit injects BR elements to
8134 		// make it possible to place the caret inside empty blocks. This logic tries to remove
8135 		// these elements and keep br elements that where intended to be there intact
8136 		if (settings.remove_trailing_brs) {
8137 			self.addNodeFilter('br', function(nodes) {
8138 				var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
8139 				var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
8140 				var elementRule, textNode;
8141 
8142 				// Remove brs from body element as well
8143 				blockElements.body = 1;
8144 
8145 				// Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
8146 				for (i = 0; i < l; i++) {
8147 					node = nodes[i];
8148 					parent = node.parent;
8149 
8150 					if (blockElements[node.parent.name] && node === parent.lastChild) {
8151 						// Loop all nodes to the left of the current node and check for other BR elements
8152 						// excluding bookmarks since they are invisible
8153 						prev = node.prev;
8154 						while (prev) {
8155 							prevName = prev.name;
8156 
8157 							// Ignore bookmarks
8158 							if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
8159 								// Found a non BR element
8160 								if (prevName !== "br") {
8161 									break;
8162 								}
8163 
8164 								// Found another br it's a <br><br> structure then don't remove anything
8165 								if (prevName === 'br') {
8166 									node = null;
8167 									break;
8168 								}
8169 							}
8170 
8171 							prev = prev.prev;
8172 						}
8173 
8174 						if (node) {
8175 							node.remove();
8176 
8177 							// Is the parent to be considered empty after we removed the BR
8178 							if (parent.isEmpty(nonEmptyElements)) {
8179 								elementRule = schema.getElementRule(parent.name);
8180 
8181 								// Remove or padd the element depending on schema rule
8182 								if (elementRule) {
8183 									if (elementRule.removeEmpty) {
8184 										parent.remove();
8185 									} else if (elementRule.paddEmpty) {
8186 										parent.empty().append(new Node('#text', 3)).value = '\u00a0';
8187 									}
8188 								}
8189 							}
8190 						}
8191 					} else {
8192 						// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
8193 						// so they become <p><b><i> </i></b></p>
8194 						lastParent = node;
8195 						while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
8196 							lastParent = parent;
8197 
8198 							if (blockElements[parent.name]) {
8199 								break;
8200 							}
8201 
8202 							parent = parent.parent;
8203 						}
8204 
8205 						if (lastParent === parent) {
8206 							textNode = new Node('#text', 3);
8207 							textNode.value = '\u00a0';
8208 							node.replace(textNode);
8209 						}
8210 					}
8211 				}
8212 			});
8213 		}
8214 
8215 		// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
8216 		if (!settings.allow_html_in_named_anchor) {
8217 			self.addAttributeFilter('id,name', function(nodes) {
8218 				var i = nodes.length, sibling, prevSibling, parent, node;
8219 
8220 				while (i--) {
8221 					node = nodes[i];
8222 					if (node.name === 'a' && node.firstChild && !node.attr('href')) {
8223 						parent = node.parent;
8224 
8225 						// Move children after current node
8226 						sibling = node.lastChild;
8227 						do {
8228 							prevSibling = sibling.prev;
8229 							parent.insert(sibling, node);
8230 							sibling = prevSibling;
8231 						} while (sibling);
8232 					}
8233 				}
8234 			});
8235 		}
8236 	};
8237 });
8238 
8239 // Included from: js/tinymce/classes/html/Writer.js
8240 
8241 /**
8242  * Writer.js
8243  *
8244  * Copyright, Moxiecode Systems AB
8245  * Released under LGPL License.
8246  *
8247  * License: http://www.tinymce.com/license
8248  * Contributing: http://www.tinymce.com/contributing
8249  */
8250 
8251 /**
8252  * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser.
8253  *
8254  * @class tinymce.html.Writer
8255  * @example
8256  * var writer = new tinymce.html.Writer({indent: true});
8257  * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>');
8258  * console.log(writer.getContent());
8259  *
8260  * @class tinymce.html.Writer
8261  * @version 3.4
8262  */
8263 define("tinymce/html/Writer", [
8264 	"tinymce/html/Entities",
8265 	"tinymce/util/Tools"
8266 ], function(Entities, Tools) {
8267 	var makeMap = Tools.makeMap;
8268 
8269 	/**
8270 	 * Constructs a new Writer instance.
8271 	 *
8272 	 * @constructor
8273 	 * @method Writer
8274 	 * @param {Object} settings Name/value settings object.
8275 	 */
8276 	return function(settings) {
8277 		var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
8278 
8279 		settings = settings || {};
8280 		indent = settings.indent;
8281 		indentBefore = makeMap(settings.indent_before || '');
8282 		indentAfter = makeMap(settings.indent_after || '');
8283 		encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
8284 		htmlOutput = settings.element_format == "html";
8285 
8286 		return {
8287 			/**
8288 			 * Writes the a start element such as <p id="a">.
8289 			 *
8290 			 * @method start
8291 			 * @param {String} name Name of the element.
8292 			 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
8293 			 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
8294 			 */
8295 			start: function(name, attrs, empty) {
8296 				var i, l, attr, value;
8297 
8298 				if (indent && indentBefore[name] && html.length > 0) {
8299 					value = html[html.length - 1];
8300 
8301 					if (value.length > 0 && value !== '\n') {
8302 						html.push('\n');
8303 					}
8304 				}
8305 
8306 				html.push('<', name);
8307 
8308 				if (attrs) {
8309 					for (i = 0, l = attrs.length; i < l; i++) {
8310 						attr = attrs[i];
8311 						html.push(' ', attr.name, '="', encode(attr.value, true), '"');
8312 					}
8313 				}
8314 
8315 				if (!empty || htmlOutput) {
8316 					html[html.length] = '>';
8317 				} else {
8318 					html[html.length] = ' />';
8319 				}
8320 
8321 				if (empty && indent && indentAfter[name] && html.length > 0) {
8322 					value = html[html.length - 1];
8323 
8324 					if (value.length > 0 && value !== '\n') {
8325 						html.push('\n');
8326 					}
8327 				}
8328 			},
8329 
8330 			/**
8331 			 * Writes the a end element such as </p>.
8332 			 *
8333 			 * @method end
8334 			 * @param {String} name Name of the element.
8335 			 */
8336 			end: function(name) {
8337 				var value;
8338 
8339 				/*if (indent && indentBefore[name] && html.length > 0) {
8340 					value = html[html.length - 1];
8341 
8342 					if (value.length > 0 && value !== '\n')
8343 						html.push('\n');
8344 				}*/
8345 
8346 				html.push('</', name, '>');
8347 
8348 				if (indent && indentAfter[name] && html.length > 0) {
8349 					value = html[html.length - 1];
8350 
8351 					if (value.length > 0 && value !== '\n') {
8352 						html.push('\n');
8353 					}
8354 				}
8355 			},
8356 
8357 			/**
8358 			 * Writes a text node.
8359 			 *
8360 			 * @method text
8361 			 * @param {String} text String to write out.
8362 			 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
8363 			 */
8364 			text: function(text, raw) {
8365 				if (text.length > 0) {
8366 					html[html.length] = raw ? text : encode(text);
8367 				}
8368 			},
8369 
8370 			/**
8371 			 * Writes a cdata node such as <![CDATA[data]]>.
8372 			 *
8373 			 * @method cdata
8374 			 * @param {String} text String to write out inside the cdata.
8375 			 */
8376 			cdata: function(text) {
8377 				html.push('<![CDATA[', text, ']]>');
8378 			},
8379 
8380 			/**
8381 			 * Writes a comment node such as <!-- Comment -->.
8382 			 *
8383 			 * @method cdata
8384 			 * @param {String} text String to write out inside the comment.
8385 			 */
8386 			comment: function(text) {
8387 				html.push('<!--', text, '-->');
8388 			},
8389 
8390 			/**
8391 			 * Writes a PI node such as <?xml attr="value" ?>.
8392 			 *
8393 			 * @method pi
8394 			 * @param {String} name Name of the pi.
8395 			 * @param {String} text String to write out inside the pi.
8396 			 */
8397 			pi: function(name, text) {
8398 				if (text) {
8399 					html.push('<?', name, ' ', text, '?>');
8400 				} else {
8401 					html.push('<?', name, '?>');
8402 				}
8403 
8404 				if (indent) {
8405 					html.push('\n');
8406 				}
8407 			},
8408 
8409 			/**
8410 			 * Writes a doctype node such as <!DOCTYPE data>.
8411 			 *
8412 			 * @method doctype
8413 			 * @param {String} text String to write out inside the doctype.
8414 			 */
8415 			doctype: function(text) {
8416 				html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
8417 			},
8418 
8419 			/**
8420 			 * Resets the internal buffer if one wants to reuse the writer.
8421 			 *
8422 			 * @method reset
8423 			 */
8424 			reset: function() {
8425 				html.length = 0;
8426 			},
8427 
8428 			/**
8429 			 * Returns the contents that got serialized.
8430 			 *
8431 			 * @method getContent
8432 			 * @return {String} HTML contents that got written down.
8433 			 */
8434 			getContent: function() {
8435 				return html.join('').replace(/\n$/, '');
8436 			}
8437 		};
8438 	};
8439 });
8440 
8441 // Included from: js/tinymce/classes/html/Serializer.js
8442 
8443 /**
8444  * Serializer.js
8445  *
8446  * Copyright, Moxiecode Systems AB
8447  * Released under LGPL License.
8448  *
8449  * License: http://www.tinymce.com/license
8450  * Contributing: http://www.tinymce.com/contributing
8451  */
8452 
8453 /**
8454  * This class is used to serialize down the DOM tree into a string using a Writer instance.
8455  *
8456  *
8457  * @example
8458  * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
8459  * @class tinymce.html.Serializer
8460  * @version 3.4
8461  */
8462 define("tinymce/html/Serializer", [
8463 	"tinymce/html/Writer",
8464 	"tinymce/html/Schema"
8465 ], function(Writer, Schema) {
8466 	/**
8467 	 * Constructs a new Serializer instance.
8468 	 *
8469 	 * @constructor
8470 	 * @method Serializer
8471 	 * @param {Object} settings Name/value settings object.
8472 	 * @param {tinymce.html.Schema} schema Schema instance to use.
8473 	 */
8474 	return function(settings, schema) {
8475 		var self = this, writer = new Writer(settings);
8476 
8477 		settings = settings || {};
8478 		settings.validate = "validate" in settings ? settings.validate : true;
8479 
8480 		self.schema = schema = schema || new Schema();
8481 		self.writer = writer;
8482 
8483 		/**
8484 		 * Serializes the specified node into a string.
8485 		 *
8486 		 * @example
8487 		 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
8488 		 * @method serialize
8489 		 * @param {tinymce.html.Node} node Node instance to serialize.
8490 		 * @return {String} String with HTML based on DOM tree.
8491 		 */
8492 		self.serialize = function(node) {
8493 			var handlers, validate;
8494 
8495 			validate = settings.validate;
8496 
8497 			handlers = {
8498 				// #text
8499 				3: function(node) {
8500 					writer.text(node.value, node.raw);
8501 				},
8502 
8503 				// #comment
8504 				8: function(node) {
8505 					writer.comment(node.value);
8506 				},
8507 
8508 				// Processing instruction
8509 				7: function(node) {
8510 					writer.pi(node.name, node.value);
8511 				},
8512 
8513 				// Doctype
8514 				10: function(node) {
8515 					writer.doctype(node.value);
8516 				},
8517 
8518 				// CDATA
8519 				4: function(node) {
8520 					writer.cdata(node.value);
8521 				},
8522 
8523 				// Document fragment
8524 				11: function(node) {
8525 					if ((node = node.firstChild)) {
8526 						do {
8527 							walk(node);
8528 						} while ((node = node.next));
8529 					}
8530 				}
8531 			};
8532 
8533 			writer.reset();
8534 
8535 			function walk(node) {
8536 				var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
8537 
8538 				if (!handler) {
8539 					name = node.name;
8540 					isEmpty = node.shortEnded;
8541 					attrs = node.attributes;
8542 
8543 					// Sort attributes
8544 					if (validate && attrs && attrs.length > 1) {
8545 						sortedAttrs = [];
8546 						sortedAttrs.map = {};
8547 
8548 						elementRule = schema.getElementRule(node.name);
8549 						for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
8550 							attrName = elementRule.attributesOrder[i];
8551 
8552 							if (attrName in attrs.map) {
8553 								attrValue = attrs.map[attrName];
8554 								sortedAttrs.map[attrName] = attrValue;
8555 								sortedAttrs.push({name: attrName, value: attrValue});
8556 							}
8557 						}
8558 
8559 						for (i = 0, l = attrs.length; i < l; i++) {
8560 							attrName = attrs[i].name;
8561 
8562 							if (!(attrName in sortedAttrs.map)) {
8563 								attrValue = attrs.map[attrName];
8564 								sortedAttrs.map[attrName] = attrValue;
8565 								sortedAttrs.push({name: attrName, value: attrValue});
8566 							}
8567 						}
8568 
8569 						attrs = sortedAttrs;
8570 					}
8571 
8572 					writer.start(node.name, attrs, isEmpty);
8573 
8574 					if (!isEmpty) {
8575 						if ((node = node.firstChild)) {
8576 							do {
8577 								walk(node);
8578 							} while ((node = node.next));
8579 						}
8580 
8581 						writer.end(name);
8582 					}
8583 				} else {
8584 					handler(node);
8585 				}
8586 			}
8587 
8588 			// Serialize element and treat all non elements as fragments
8589 			if (node.type == 1 && !settings.inner) {
8590 				walk(node);
8591 			} else {
8592 				handlers[11](node);
8593 			}
8594 
8595 			return writer.getContent();
8596 		};
8597 	};
8598 });
8599 
8600 // Included from: js/tinymce/classes/dom/Serializer.js
8601 
8602 /**
8603  * Serializer.js
8604  *
8605  * Copyright, Moxiecode Systems AB
8606  * Released under LGPL License.
8607  *
8608  * License: http://www.tinymce.com/license
8609  * Contributing: http://www.tinymce.com/contributing
8610  */
8611 
8612 /**
8613  * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for
8614  * more details and examples on how to use this class.
8615  *
8616  * @class tinymce.dom.Serializer
8617  */
8618 define("tinymce/dom/Serializer", [
8619 	"tinymce/dom/DOMUtils",
8620 	"tinymce/html/DomParser",
8621 	"tinymce/html/Entities",
8622 	"tinymce/html/Serializer",
8623 	"tinymce/html/Node",
8624 	"tinymce/html/Schema",
8625 	"tinymce/Env",
8626 	"tinymce/util/Tools"
8627 ], function(DOMUtils, DomParser, Entities, Serializer, Node, Schema, Env, Tools) {
8628 	var each = Tools.each, trim = Tools.trim;
8629 	var DOM = DOMUtils.DOM;
8630 
8631 	/**
8632 	 * Constructs a new DOM serializer class.
8633 	 *
8634 	 * @constructor
8635 	 * @method Serializer
8636 	 * @param {Object} settings Serializer settings object.
8637 	 * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
8638 	 */
8639 	return function(settings, editor) {
8640 		var dom, schema, htmlParser;
8641 
8642 		if (editor) {
8643 			dom = editor.dom;
8644 			schema = editor.schema;
8645 		}
8646 
8647 		// Default DOM and Schema if they are undefined
8648 		dom = dom || DOM;
8649 		schema = schema || new Schema(settings);
8650 		settings.entity_encoding = settings.entity_encoding || 'named';
8651 		settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
8652 
8653 		htmlParser = new DomParser(settings, schema);
8654 
8655 		// Convert tabindex back to elements when serializing contents
8656 		htmlParser.addAttributeFilter('data-mce-tabindex', function(nodes, name) {
8657 			var i = nodes.length, node;
8658 
8659 			while (i--) {
8660 				node = nodes[i];
8661 				node.attr('tabindex', node.attributes.map['data-mce-tabindex']);
8662 				node.attr(name, null);
8663 			}
8664 		});
8665 
8666 		// Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
8667 		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
8668 			var i = nodes.length, node, value, internalName = 'data-mce-' + name;
8669 			var urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
8670 
8671 			while (i--) {
8672 				node = nodes[i];
8673 
8674 				value = node.attributes.map[internalName];
8675 				if (value !== undef) {
8676 					// Set external name to internal value and remove internal
8677 					node.attr(name, value.length > 0 ? value : null);
8678 					node.attr(internalName, null);
8679 				} else {
8680 					// No internal attribute found then convert the value we have in the DOM
8681 					value = node.attributes.map[name];
8682 
8683 					if (name === "style") {
8684 						value = dom.serializeStyle(dom.parseStyle(value), node.name);
8685 					} else if (urlConverter) {
8686 						value = urlConverter.call(urlConverterScope, value, name, node.name);
8687 					}
8688 
8689 					node.attr(name, value.length > 0 ? value : null);
8690 				}
8691 			}
8692 		});
8693 
8694 		// Remove internal classes mceItem<..> or mceSelected
8695 		htmlParser.addAttributeFilter('class', function(nodes) {
8696 			var i = nodes.length, node, value;
8697 
8698 			while (i--) {
8699 				node = nodes[i];
8700 				value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
8701 				node.attr('class', value.length > 0 ? value : null);
8702 			}
8703 		});
8704 
8705 		// Remove bookmark elements
8706 		htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
8707 			var i = nodes.length, node;
8708 
8709 			while (i--) {
8710 				node = nodes[i];
8711 
8712 				if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) {
8713 					node.remove();
8714 				}
8715 			}
8716 		});
8717 
8718 		// Remove expando attributes
8719 		htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name) {
8720 			var i = nodes.length;
8721 
8722 			while (i--) {
8723 				nodes[i].attr(name, null);
8724 			}
8725 		});
8726 
8727 		htmlParser.addNodeFilter('noscript', function(nodes) {
8728 			var i = nodes.length, node;
8729 
8730 			while (i--) {
8731 				node = nodes[i].firstChild;
8732 
8733 				if (node) {
8734 					node.value = Entities.decode(node.value);
8735 				}
8736 			}
8737 		});
8738 
8739 		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
8740 		htmlParser.addNodeFilter('script,style', function(nodes, name) {
8741 			var i = nodes.length, node, value;
8742 
8743 			function trim(value) {
8744 				/*jshint maxlen:255 */
8745 				/*eslint max-len:0 */
8746 				return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
8747 						.replace(/^[\r\n]*|[\r\n]*$/g, '')
8748 						.replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
8749 						.replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
8750 			}
8751 
8752 			while (i--) {
8753 				node = nodes[i];
8754 				value = node.firstChild ? node.firstChild.value : '';
8755 
8756 				if (name === "script") {
8757 					// Remove mce- prefix from script elements and remove default text/javascript mime type (HTML5)
8758 					var type = (node.attr('type') || 'text/javascript').replace(/^mce\-/, '');
8759 					node.attr('type', type === 'text/javascript' ? null : type);
8760 
8761 					if (value.length > 0) {
8762 						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
8763 					}
8764 				} else {
8765 					if (value.length > 0) {
8766 						node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
8767 					}
8768 				}
8769 			}
8770 		});
8771 
8772 		// Convert comments to cdata and handle protected comments
8773 		htmlParser.addNodeFilter('#comment', function(nodes) {
8774 			var i = nodes.length, node;
8775 
8776 			while (i--) {
8777 				node = nodes[i];
8778 
8779 				if (node.value.indexOf('[CDATA[') === 0) {
8780 					node.name = '#cdata';
8781 					node.type = 4;
8782 					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
8783 				} else if (node.value.indexOf('mce:protected ') === 0) {
8784 					node.name = "#text";
8785 					node.type = 3;
8786 					node.raw = true;
8787 					node.value = unescape(node.value).substr(14);
8788 				}
8789 			}
8790 		});
8791 
8792 		htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
8793 			var i = nodes.length, node;
8794 
8795 			while (i--) {
8796 				node = nodes[i];
8797 				if (node.type === 7) {
8798 					node.remove();
8799 				} else if (node.type === 1) {
8800 					if (name === "input" && !("type" in node.attributes.map)) {
8801 						node.attr('type', 'text');
8802 					}
8803 				}
8804 			}
8805 		});
8806 
8807 		// Fix list elements, TODO: Replace this later
8808 		if (settings.fix_list_elements) {
8809 			htmlParser.addNodeFilter('ul,ol', function(nodes) {
8810 				var i = nodes.length, node, parentNode;
8811 
8812 				while (i--) {
8813 					node = nodes[i];
8814 					parentNode = node.parent;
8815 
8816 					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
8817 						if (node.prev && node.prev.name === 'li') {
8818 							node.prev.append(node);
8819 						}
8820 					}
8821 				}
8822 			});
8823 		}
8824 
8825 		// Remove internal data attributes
8826 		htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style,data-mce-selected', function(nodes, name) {
8827 			var i = nodes.length;
8828 
8829 			while (i--) {
8830 				nodes[i].attr(name, null);
8831 			}
8832 		});
8833 
8834 		// Return public methods
8835 		return {
8836 			/**
8837 			 * Schema instance that was used to when the Serializer was constructed.
8838 			 *
8839 			 * @field {tinymce.html.Schema} schema
8840 			 */
8841 			schema: schema,
8842 
8843 			/**
8844 			 * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name
8845 			 * and then execute the callback ones it has finished parsing the document.
8846 			 *
8847 			 * @example
8848 			 * parser.addNodeFilter('p,h1', function(nodes, name) {
8849 			 *		for (var i = 0; i < nodes.length; i++) {
8850 			 *			console.log(nodes[i].name);
8851 			 *		}
8852 			 * });
8853 			 * @method addNodeFilter
8854 			 * @method {String} name Comma separated list of nodes to collect.
8855 			 * @param {function} callback Callback function to execute once it has collected nodes.
8856 			 */
8857 			addNodeFilter: htmlParser.addNodeFilter,
8858 
8859 			/**
8860 			 * Adds a attribute filter function to the parser used by the serializer, the parser will
8861 			 * collect nodes that has the specified attributes
8862 			 * and then execute the callback ones it has finished parsing the document.
8863 			 *
8864 			 * @example
8865 			 * parser.addAttributeFilter('src,href', function(nodes, name) {
8866 			 *		for (var i = 0; i < nodes.length; i++) {
8867 			 *			console.log(nodes[i].name);
8868 			 *		}
8869 			 * });
8870 			 * @method addAttributeFilter
8871 			 * @method {String} name Comma separated list of nodes to collect.
8872 			 * @param {function} callback Callback function to execute once it has collected nodes.
8873 			 */
8874 			addAttributeFilter: htmlParser.addAttributeFilter,
8875 
8876 			/**
8877 			 * Serializes the specified browser DOM node into a HTML string.
8878 			 *
8879 			 * @method serialize
8880 			 * @param {DOMNode} node DOM node to serialize.
8881 			 * @param {Object} args Arguments option that gets passed to event handlers.
8882 			 */
8883 			serialize: function(node, args) {
8884 				var self = this, impl, doc, oldDoc, htmlSerializer, content;
8885 
8886 				// Explorer won't clone contents of script and style and the
8887 				// selected index of select elements are cleared on a clone operation.
8888 				if (Env.ie && dom.select('script,style,select,map').length > 0) {
8889 					content = node.innerHTML;
8890 					node = node.cloneNode(false);
8891 					dom.setHTML(node, content);
8892 				} else {
8893 					node = node.cloneNode(true);
8894 				}
8895 
8896 				// Nodes needs to be attached to something in WebKit/Opera
8897 				// This fix will make DOM ranges and make Sizzle happy!
8898 				impl = node.ownerDocument.implementation;
8899 				if (impl.createHTMLDocument) {
8900 					// Create an empty HTML document
8901 					doc = impl.createHTMLDocument("");
8902 
8903 					// Add the element or it's children if it's a body element to the new document
8904 					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
8905 						doc.body.appendChild(doc.importNode(node, true));
8906 					});
8907 
8908 					// Grab first child or body element for serialization
8909 					if (node.nodeName != 'BODY') {
8910 						node = doc.body.firstChild;
8911 					} else {
8912 						node = doc.body;
8913 					}
8914 
8915 					// set the new document in DOMUtils so createElement etc works
8916 					oldDoc = dom.doc;
8917 					dom.doc = doc;
8918 				}
8919 
8920 				args = args || {};
8921 				args.format = args.format || 'html';
8922 
8923 				// Don't wrap content if we want selected html
8924 				if (args.selection) {
8925 					args.forced_root_block = '';
8926 				}
8927 
8928 				// Pre process
8929 				if (!args.no_events) {
8930 					args.node = node;
8931 					self.onPreProcess(args);
8932 				}
8933 
8934 				// Setup serializer
8935 				htmlSerializer = new Serializer(settings, schema);
8936 
8937 				// Parse and serialize HTML
8938 				args.content = htmlSerializer.serialize(
8939 					htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
8940 				);
8941 
8942 				// Replace all BOM characters for now until we can find a better solution
8943 				if (!args.cleanup) {
8944 					args.content = args.content.replace(/\uFEFF/g, '');
8945 				}
8946 
8947 				// Post process
8948 				if (!args.no_events) {
8949 					self.onPostProcess(args);
8950 				}
8951 
8952 				// Restore the old document if it was changed
8953 				if (oldDoc) {
8954 					dom.doc = oldDoc;
8955 				}
8956 
8957 				args.node = null;
8958 
8959 				return args.content;
8960 			},
8961 
8962 			/**
8963 			 * Adds valid elements rules to the serializers schema instance this enables you to specify things
8964 			 * like what elements should be outputted and what attributes specific elements might have.
8965 			 * Consult the Wiki for more details on this format.
8966 			 *
8967 			 * @method addRules
8968 			 * @param {String} rules Valid elements rules string to add to schema.
8969 			 */
8970 			addRules: function(rules) {
8971 				schema.addValidElements(rules);
8972 			},
8973 
8974 			/**
8975 			 * Sets the valid elements rules to the serializers schema instance this enables you to specify things
8976 			 * like what elements should be outputted and what attributes specific elements might have.
8977 			 * Consult the Wiki for more details on this format.
8978 			 *
8979 			 * @method setRules
8980 			 * @param {String} rules Valid elements rules string.
8981 			 */
8982 			setRules: function(rules) {
8983 				schema.setValidElements(rules);
8984 			},
8985 
8986 			onPreProcess: function(args) {
8987 				if (editor) {
8988 					editor.fire('PreProcess', args);
8989 				}
8990 			},
8991 
8992 			onPostProcess: function(args) {
8993 				if (editor) {
8994 					editor.fire('PostProcess', args);
8995 				}
8996 			}
8997 		};
8998 	};
8999 });
9000 
9001 // Included from: js/tinymce/classes/dom/TridentSelection.js
9002 
9003 /**
9004  * TridentSelection.js
9005  *
9006  * Copyright, Moxiecode Systems AB
9007  * Released under LGPL License.
9008  *
9009  * License: http://www.tinymce.com/license
9010  * Contributing: http://www.tinymce.com/contributing
9011  */
9012 
9013 /**
9014  * Selection class for old explorer versions. This one fakes the
9015  * native selection object available on modern browsers.
9016  *
9017  * @class tinymce.dom.TridentSelection
9018  */
9019 define("tinymce/dom/TridentSelection", [], function() {
9020 	function Selection(selection) {
9021 		var self = this, dom = selection.dom, FALSE = false;
9022 
9023 		function getPosition(rng, start) {
9024 			var checkRng, startIndex = 0, endIndex, inside,
9025 				children, child, offset, index, position = -1, parent;
9026 
9027 			// Setup test range, collapse it and get the parent
9028 			checkRng = rng.duplicate();
9029 			checkRng.collapse(start);
9030 			parent = checkRng.parentElement();
9031 
9032 			// Check if the selection is within the right document
9033 			if (parent.ownerDocument !== selection.dom.doc) {
9034 				return;
9035 			}
9036 
9037 			// IE will report non editable elements as it's parent so look for an editable one
9038 			while (parent.contentEditable === "false") {
9039 				parent = parent.parentNode;
9040 			}
9041 
9042 			// If parent doesn't have any children then return that we are inside the element
9043 			if (!parent.hasChildNodes()) {
9044 				return {node: parent, inside: 1};
9045 			}
9046 
9047 			// Setup node list and endIndex
9048 			children = parent.children;
9049 			endIndex = children.length - 1;
9050 
9051 			// Perform a binary search for the position
9052 			while (startIndex <= endIndex) {
9053 				index = Math.floor((startIndex + endIndex) / 2);
9054 
9055 				// Move selection to node and compare the ranges
9056 				child = children[index];
9057 				checkRng.moveToElementText(child);
9058 				position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
9059 
9060 				// Before/after or an exact match
9061 				if (position > 0) {
9062 					endIndex = index - 1;
9063 				} else if (position < 0) {
9064 					startIndex = index + 1;
9065 				} else {
9066 					return {node: child};
9067 				}
9068 			}
9069 
9070 			// Check if child position is before or we didn't find a position
9071 			if (position < 0) {
9072 				// No element child was found use the parent element and the offset inside that
9073 				if (!child) {
9074 					checkRng.moveToElementText(parent);
9075 					checkRng.collapse(true);
9076 					child = parent;
9077 					inside = true;
9078 				} else {
9079 					checkRng.collapse(false);
9080 				}
9081 
9082 				// Walk character by character in text node until we hit the selected range endpoint,
9083 				// hit the end of document or parent isn't the right one
9084 				// We need to walk char by char since rng.text or rng.htmlText will trim line endings
9085 				offset = 0;
9086 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
9087 					if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
9088 						break;
9089 					}
9090 
9091 					offset++;
9092 				}
9093 			} else {
9094 				// Child position is after the selection endpoint
9095 				checkRng.collapse(true);
9096 
9097 				// Walk character by character in text node until we hit the selected range endpoint, hit
9098 				// the end of document or parent isn't the right one
9099 				offset = 0;
9100 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
9101 					if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
9102 						break;
9103 					}
9104 
9105 					offset++;
9106 				}
9107 			}
9108 
9109 			return {node: child, position: position, offset: offset, inside: inside};
9110 		}
9111 
9112 		// Returns a W3C DOM compatible range object by using the IE Range API
9113 		function getRange() {
9114 			var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
9115 
9116 			// If selection is outside the current document just return an empty range
9117 			element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
9118 			if (element.ownerDocument != dom.doc) {
9119 				return domRange;
9120 			}
9121 
9122 			collapsed = selection.isCollapsed();
9123 
9124 			// Handle control selection
9125 			if (ieRange.item) {
9126 				domRange.setStart(element.parentNode, dom.nodeIndex(element));
9127 				domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
9128 
9129 				return domRange;
9130 			}
9131 
9132 			function findEndPoint(start) {
9133 				var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
9134 
9135 				container = endPoint.node;
9136 				offset = endPoint.offset;
9137 
9138 				if (endPoint.inside && !container.hasChildNodes()) {
9139 					domRange[start ? 'setStart' : 'setEnd'](container, 0);
9140 					return;
9141 				}
9142 
9143 				if (offset === undef) {
9144 					domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
9145 					return;
9146 				}
9147 
9148 				if (endPoint.position < 0) {
9149 					sibling = endPoint.inside ? container.firstChild : container.nextSibling;
9150 
9151 					if (!sibling) {
9152 						domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
9153 						return;
9154 					}
9155 
9156 					if (!offset) {
9157 						if (sibling.nodeType == 3) {
9158 							domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
9159 						} else {
9160 							domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
9161 						}
9162 
9163 						return;
9164 					}
9165 
9166 					// Find the text node and offset
9167 					while (sibling) {
9168 						nodeValue = sibling.nodeValue;
9169 						textNodeOffset += nodeValue.length;
9170 
9171 						// We are at or passed the position we where looking for
9172 						if (textNodeOffset >= offset) {
9173 							container = sibling;
9174 							textNodeOffset -= offset;
9175 							textNodeOffset = nodeValue.length - textNodeOffset;
9176 							break;
9177 						}
9178 
9179 						sibling = sibling.nextSibling;
9180 					}
9181 				} else {
9182 					// Find the text node and offset
9183 					sibling = container.previousSibling;
9184 
9185 					if (!sibling) {
9186 						return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
9187 					}
9188 
9189 					// If there isn't any text to loop then use the first position
9190 					if (!offset) {
9191 						if (container.nodeType == 3) {
9192 							domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
9193 						} else {
9194 							domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
9195 						}
9196 
9197 						return;
9198 					}
9199 
9200 					while (sibling) {
9201 						textNodeOffset += sibling.nodeValue.length;
9202 
9203 						// We are at or passed the position we where looking for
9204 						if (textNodeOffset >= offset) {
9205 							container = sibling;
9206 							textNodeOffset -= offset;
9207 							break;
9208 						}
9209 
9210 						sibling = sibling.previousSibling;
9211 					}
9212 				}
9213 
9214 				domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
9215 			}
9216 
9217 			try {
9218 				// Find start point
9219 				findEndPoint(true);
9220 
9221 				// Find end point if needed
9222 				if (!collapsed) {
9223 					findEndPoint();
9224 				}
9225 			} catch (ex) {
9226 				// IE has a nasty bug where text nodes might throw "invalid argument" when you
9227 				// access the nodeValue or other properties of text nodes. This seems to happend when
9228 				// text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
9229 				if (ex.number == -2147024809) {
9230 					// Get the current selection
9231 					bookmark = self.getBookmark(2);
9232 
9233 					// Get start element
9234 					tmpRange = ieRange.duplicate();
9235 					tmpRange.collapse(true);
9236 					element = tmpRange.parentElement();
9237 
9238 					// Get end element
9239 					if (!collapsed) {
9240 						tmpRange = ieRange.duplicate();
9241 						tmpRange.collapse(false);
9242 						element2 = tmpRange.parentElement();
9243 						element2.innerHTML = element2.innerHTML;
9244 					}
9245 
9246 					// Remove the broken elements
9247 					element.innerHTML = element.innerHTML;
9248 
9249 					// Restore the selection
9250 					self.moveToBookmark(bookmark);
9251 
9252 					// Since the range has moved we need to re-get it
9253 					ieRange = selection.getRng();
9254 
9255 					// Find start point
9256 					findEndPoint(true);
9257 
9258 					// Find end point if needed
9259 					if (!collapsed) {
9260 						findEndPoint();
9261 					}
9262 				} else {
9263 					throw ex; // Throw other errors
9264 				}
9265 			}
9266 
9267 			return domRange;
9268 		}
9269 
9270 		this.getBookmark = function(type) {
9271 			var rng = selection.getRng(), bookmark = {};
9272 
9273 			function getIndexes(node) {
9274 				var parent, root, children, i, indexes = [];
9275 
9276 				parent = node.parentNode;
9277 				root = dom.getRoot().parentNode;
9278 
9279 				while (parent != root && parent.nodeType !== 9) {
9280 					children = parent.children;
9281 
9282 					i = children.length;
9283 					while (i--) {
9284 						if (node === children[i]) {
9285 							indexes.push(i);
9286 							break;
9287 						}
9288 					}
9289 
9290 					node = parent;
9291 					parent = parent.parentNode;
9292 				}
9293 
9294 				return indexes;
9295 			}
9296 
9297 			function getBookmarkEndPoint(start) {
9298 				var position;
9299 
9300 				position = getPosition(rng, start);
9301 				if (position) {
9302 					return {
9303 						position: position.position,
9304 						offset: position.offset,
9305 						indexes: getIndexes(position.node),
9306 						inside: position.inside
9307 					};
9308 				}
9309 			}
9310 
9311 			// Non ubstructive bookmark
9312 			if (type === 2) {
9313 				// Handle text selection
9314 				if (!rng.item) {
9315 					bookmark.start = getBookmarkEndPoint(true);
9316 
9317 					if (!selection.isCollapsed()) {
9318 						bookmark.end = getBookmarkEndPoint();
9319 					}
9320 				} else {
9321 					bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
9322 				}
9323 			}
9324 
9325 			return bookmark;
9326 		};
9327 
9328 		this.moveToBookmark = function(bookmark) {
9329 			var rng, body = dom.doc.body;
9330 
9331 			function resolveIndexes(indexes) {
9332 				var node, i, idx, children;
9333 
9334 				node = dom.getRoot();
9335 				for (i = indexes.length - 1; i >= 0; i--) {
9336 					children = node.children;
9337 					idx = indexes[i];
9338 
9339 					if (idx <= children.length - 1) {
9340 						node = children[idx];
9341 					}
9342 				}
9343 
9344 				return node;
9345 			}
9346 
9347 			function setBookmarkEndPoint(start) {
9348 				var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
9349 
9350 				if (endPoint) {
9351 					moveLeft = endPoint.position > 0;
9352 
9353 					moveRng = body.createTextRange();
9354 					moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
9355 
9356 					offset = endPoint.offset;
9357 					if (offset !== undef) {
9358 						moveRng.collapse(endPoint.inside || moveLeft);
9359 						moveRng.moveStart('character', moveLeft ? -offset : offset);
9360 					} else {
9361 						moveRng.collapse(start);
9362 					}
9363 
9364 					rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
9365 
9366 					if (start) {
9367 						rng.collapse(true);
9368 					}
9369 				}
9370 			}
9371 
9372 			if (bookmark.start) {
9373 				if (bookmark.start.ctrl) {
9374 					rng = body.createControlRange();
9375 					rng.addElement(resolveIndexes(bookmark.start.indexes));
9376 					rng.select();
9377 				} else {
9378 					rng = body.createTextRange();
9379 					setBookmarkEndPoint(true);
9380 					setBookmarkEndPoint();
9381 					rng.select();
9382 				}
9383 			}
9384 		};
9385 
9386 		this.addRange = function(rng) {
9387 			var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
9388 				doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
9389 
9390 			function setEndPoint(start) {
9391 				var container, offset, marker, tmpRng, nodes;
9392 
9393 				marker = dom.create('a');
9394 				container = start ? startContainer : endContainer;
9395 				offset = start ? startOffset : endOffset;
9396 				tmpRng = ieRng.duplicate();
9397 
9398 				if (container == doc || container == doc.documentElement) {
9399 					container = body;
9400 					offset = 0;
9401 				}
9402 
9403 				if (container.nodeType == 3) {
9404 					container.parentNode.insertBefore(marker, container);
9405 					tmpRng.moveToElementText(marker);
9406 					tmpRng.moveStart('character', offset);
9407 					dom.remove(marker);
9408 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
9409 				} else {
9410 					nodes = container.childNodes;
9411 
9412 					if (nodes.length) {
9413 						if (offset >= nodes.length) {
9414 							dom.insertAfter(marker, nodes[nodes.length - 1]);
9415 						} else {
9416 							container.insertBefore(marker, nodes[offset]);
9417 						}
9418 
9419 						tmpRng.moveToElementText(marker);
9420 					} else if (container.canHaveHTML) {
9421 						// Empty node selection for example <div>|</div>
9422 						// Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
9423 						container.innerHTML = '<span></span>';
9424 						marker = container.firstChild;
9425 						tmpRng.moveToElementText(marker);
9426 						tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
9427 					}
9428 
9429 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
9430 					dom.remove(marker);
9431 				}
9432 			}
9433 
9434 			// Setup some shorter versions
9435 			startContainer = rng.startContainer;
9436 			startOffset = rng.startOffset;
9437 			endContainer = rng.endContainer;
9438 			endOffset = rng.endOffset;
9439 			ieRng = body.createTextRange();
9440 
9441 			// If single element selection then try making a control selection out of it
9442 			if (startContainer == endContainer && startContainer.nodeType == 1) {
9443 				// Trick to place the caret inside an empty block element like <p></p>
9444 				if (startOffset == endOffset && !startContainer.hasChildNodes()) {
9445 					if (startContainer.canHaveHTML) {
9446 						// Check if previous sibling is an empty block if it is then we need to render it
9447 						// IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
9448 						// Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
9449 						sibling = startContainer.previousSibling;
9450 						if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
9451 							sibling.innerHTML = '';
9452 						} else {
9453 							sibling = null;
9454 						}
9455 
9456 						startContainer.innerHTML = '<span></span><span></span>';
9457 						ieRng.moveToElementText(startContainer.lastChild);
9458 						ieRng.select();
9459 						dom.doc.selection.clear();
9460 						startContainer.innerHTML = '';
9461 
9462 						if (sibling) {
9463 							sibling.innerHTML = '';
9464 						}
9465 						return;
9466 					} else {
9467 						startOffset = dom.nodeIndex(startContainer);
9468 						startContainer = startContainer.parentNode;
9469 					}
9470 				}
9471 
9472 				if (startOffset == endOffset - 1) {
9473 					try {
9474 						ctrlElm = startContainer.childNodes[startOffset];
9475 						ctrlRng = body.createControlRange();
9476 						ctrlRng.addElement(ctrlElm);
9477 						ctrlRng.select();
9478 
9479 						// Check if the range produced is on the correct element and is a control range
9480 						// On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
9481 						nativeRng = selection.getRng();
9482 						if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
9483 							return;
9484 						}
9485 					} catch (ex) {
9486 						// Ignore
9487 					}
9488 				}
9489 			}
9490 
9491 			// Set start/end point of selection
9492 			setEndPoint(true);
9493 			setEndPoint();
9494 
9495 			// Select the new range and scroll it into view
9496 			ieRng.select();
9497 		};
9498 
9499 		// Expose range method
9500 		this.getRangeAt = getRange;
9501 	}
9502 
9503 	return Selection;
9504 });
9505 
9506 // Included from: js/tinymce/classes/util/VK.js
9507 
9508 /**
9509  * VK.js
9510  *
9511  * Copyright, Moxiecode Systems AB
9512  * Released under LGPL License.
9513  *
9514  * License: http://www.tinymce.com/license
9515  * Contributing: http://www.tinymce.com/contributing
9516  */
9517 
9518 /**
9519  * This file exposes a set of the common KeyCodes for use.  Please grow it as needed.
9520  */
9521 define("tinymce/util/VK", [
9522 	"tinymce/Env"
9523 ], function(Env) {
9524 	return {
9525 		BACKSPACE: 8,
9526 		DELETE: 46,
9527 		DOWN: 40,
9528 		ENTER: 13,
9529 		LEFT: 37,
9530 		RIGHT: 39,
9531 		SPACEBAR: 32,
9532 		TAB: 9,
9533 		UP: 38,
9534 
9535 		modifierPressed: function(e) {
9536 			return e.shiftKey || e.ctrlKey || e.altKey;
9537 		},
9538 
9539 		metaKeyPressed: function(e) {
9540 			// Check if ctrl or meta key is pressed also check if alt is false for Polish users
9541 			return (Env.mac ? e.metaKey : e.ctrlKey) && !e.altKey;
9542 		}
9543 	};
9544 });
9545 
9546 // Included from: js/tinymce/classes/dom/ControlSelection.js
9547 
9548 /**
9549  * ControlSelection.js
9550  *
9551  * Copyright, Moxiecode Systems AB
9552  * Released under LGPL License.
9553  *
9554  * License: http://www.tinymce.com/license
9555  * Contributing: http://www.tinymce.com/contributing
9556  */
9557 
9558 /**
9559  * This class handles control selection of elements. Controls are elements
9560  * that can be resized and needs to be selected as a whole. It adds custom resize handles
9561  * to all browser engines that support properly disabling the built in resize logic.
9562  *
9563  * @class tinymce.dom.ControlSelection
9564  */
9565 define("tinymce/dom/ControlSelection", [
9566 	"tinymce/util/VK",
9567 	"tinymce/util/Tools",
9568 	"tinymce/Env"
9569 ], function(VK, Tools, Env) {
9570 	return function(selection, editor) {
9571 		var dom = editor.dom, each = Tools.each;
9572 		var selectedElm, selectedElmGhost, resizeHandles, selectedHandle, lastMouseDownEvent;
9573 		var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
9574 		var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
9575 
9576 		// Details about each resize handle how to scale etc
9577 		resizeHandles = {
9578 			// Name: x multiplier, y multiplier, delta size x, delta size y
9579 			n:  [0.5,   0,     0,   -1],
9580 			e:  [1,    0.5,    1,    0],
9581 			s:  [0.5,   1,     0,    1],
9582 			w:  [0,    0.5,   -1,    0],
9583 			nw: [0,     0,    -1,   -1],
9584 			ne: [1,     0,     1,   -1],
9585 			se: [1,     1,     1,    1],
9586 			sw: [0,     1,    -1,    1]
9587 		};
9588 
9589 		// Add CSS for resize handles, cloned element and selected
9590 		var rootClass = '.mce-content-body';
9591 		editor.contentStyles.push(
9592 			rootClass + ' div.mce-resizehandle {' +
9593 				'position: absolute;' +
9594 				'border: 1px solid black;' +
9595 				'background: #FFF;' +
9596 				'width: 5px;' +
9597 				'height: 5px;' +
9598 				'z-index: 10000' +
9599 			'}' +
9600 			rootClass + ' .mce-resizehandle:hover {' +
9601 				'background: #000' +
9602 			'}' +
9603 			rootClass + ' img[data-mce-selected], hr[data-mce-selected] {' +
9604 				'outline: 1px solid black;' +
9605 				'resize: none' + // Have been talks about implementing this in browsers
9606 			'}' +
9607 			rootClass + ' .mce-clonedresizable {' +
9608 				'position: absolute;' +
9609 				(Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing
9610 				'opacity: .5;' +
9611 				'filter: alpha(opacity=50);' +
9612 				'z-index: 10000' +
9613 			'}'
9614 		);
9615 
9616 		function isResizable(elm) {
9617 			var selector = editor.settings.object_resizing;
9618 
9619 			if (selector === false || Env.iOS) {
9620 				return false;
9621 			}
9622 
9623 			if (typeof selector != 'string') {
9624 				selector = 'table,img,div';
9625 			}
9626 
9627 			if (elm.getAttribute('data-mce-resize') === 'false') {
9628 				return false;
9629 			}
9630 
9631 			return editor.dom.is(elm, selector);
9632 		}
9633 
9634 		function resizeGhostElement(e) {
9635 			var deltaX, deltaY;
9636 
9637 			// Calc new width/height
9638 			deltaX = e.screenX - startX;
9639 			deltaY = e.screenY - startY;
9640 
9641 			// Calc new size
9642 			width = deltaX * selectedHandle[2] + startW;
9643 			height = deltaY * selectedHandle[3] + startH;
9644 
9645 			// Never scale down lower than 5 pixels
9646 			width = width < 5 ? 5 : width;
9647 			height = height < 5 ? 5 : height;
9648 
9649 			// Constrain proportions when modifier key is pressed or if the nw, ne, sw, se corners are moved on an image
9650 			if (VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0)) {
9651 				width = Math.round(height / ratio);
9652 				height = Math.round(width * ratio);
9653 			}
9654 
9655 			// Update ghost size
9656 			dom.setStyles(selectedElmGhost, {
9657 				width: width,
9658 				height: height
9659 			});
9660 
9661 			// Update ghost X position if needed
9662 			if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
9663 				dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
9664 			}
9665 
9666 			// Update ghost Y position if needed
9667 			if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
9668 				dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
9669 			}
9670 
9671 			if (!resizeStarted) {
9672 				editor.fire('ObjectResizeStart', {target: selectedElm, width: startW, height: startH});
9673 				resizeStarted = true;
9674 			}
9675 		}
9676 
9677 		function endGhostResize() {
9678 			resizeStarted = false;
9679 
9680 			function setSizeProp(name, value) {
9681 				if (value) {
9682 					// Resize by using style or attribute
9683 					if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
9684 						dom.setStyle(selectedElm, name, value);
9685 					} else {
9686 						dom.setAttrib(selectedElm, name, value);
9687 					}
9688 				}
9689 			}
9690 
9691 			// Set width/height properties
9692 			setSizeProp('width', width);
9693 			setSizeProp('height', height);
9694 
9695 			dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
9696 			dom.unbind(editableDoc, 'mouseup', endGhostResize);
9697 
9698 			if (rootDocument != editableDoc) {
9699 				dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
9700 				dom.unbind(rootDocument, 'mouseup', endGhostResize);
9701 			}
9702 
9703 			// Remove ghost and update resize handle positions
9704 			dom.remove(selectedElmGhost);
9705 
9706 			if (!isIE || selectedElm.nodeName == "TABLE") {
9707 				showResizeRect(selectedElm);
9708 			}
9709 
9710 			editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
9711 			editor.nodeChanged();
9712 		}
9713 
9714 		function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
9715 			var position, targetWidth, targetHeight, e, rect, offsetParent = editor.getBody();
9716 
9717 			unbindResizeHandleEvents();
9718 
9719 			// Get position and size of target
9720 			position = dom.getPos(targetElm, offsetParent);
9721 			selectedElmX = position.x;
9722 			selectedElmY = position.y;
9723 			rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
9724 			targetWidth = rect.width || (rect.right - rect.left);
9725 			targetHeight = rect.height || (rect.bottom - rect.top);
9726 
9727 			// Reset width/height if user selects a new image/table
9728 			if (selectedElm != targetElm) {
9729 				detachResizeStartListener();
9730 				selectedElm = targetElm;
9731 				width = height = 0;
9732 			}
9733 
9734 			// Makes it possible to disable resizing
9735 			e = editor.fire('ObjectSelected', {target: targetElm});
9736 
9737 			if (isResizable(targetElm) && !e.isDefaultPrevented()) {
9738 				each(resizeHandles, function(handle, name) {
9739 					var handleElm, handlerContainerElm;
9740 
9741 					function startDrag(e) {
9742 						startX = e.screenX;
9743 						startY = e.screenY;
9744 						startW = selectedElm.clientWidth;
9745 						startH = selectedElm.clientHeight;
9746 						ratio = startH / startW;
9747 						selectedHandle = handle;
9748 
9749 						selectedElmGhost = selectedElm.cloneNode(true);
9750 						dom.addClass(selectedElmGhost, 'mce-clonedresizable');
9751 						selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
9752 						selectedElmGhost.unSelectabe = true;
9753 						dom.setStyles(selectedElmGhost, {
9754 							left: selectedElmX,
9755 							top: selectedElmY,
9756 							margin: 0
9757 						});
9758 
9759 						selectedElmGhost.removeAttribute('data-mce-selected');
9760 						editor.getBody().appendChild(selectedElmGhost);
9761 
9762 						dom.bind(editableDoc, 'mousemove', resizeGhostElement);
9763 						dom.bind(editableDoc, 'mouseup', endGhostResize);
9764 
9765 						if (rootDocument != editableDoc) {
9766 							dom.bind(rootDocument, 'mousemove', resizeGhostElement);
9767 							dom.bind(rootDocument, 'mouseup', endGhostResize);
9768 						}
9769 					}
9770 
9771 					if (mouseDownHandleName) {
9772 						// Drag started by IE native resizestart
9773 						if (name == mouseDownHandleName) {
9774 							startDrag(mouseDownEvent);
9775 						}
9776 
9777 						return;
9778 					}
9779 
9780 					// Get existing or render resize handle
9781 					handleElm = dom.get('mceResizeHandle' + name);
9782 					if (!handleElm) {
9783 						handlerContainerElm = editor.getBody();
9784 
9785 						handleElm = dom.add(handlerContainerElm, 'div', {
9786 							id: 'mceResizeHandle' + name,
9787 							'data-mce-bogus': true,
9788 							'class': 'mce-resizehandle',
9789 							unselectable: true,
9790 							style: 'cursor:' + name + '-resize; margin:0; padding:0'
9791 						});
9792 
9793 						// Hides IE move layer cursor
9794 						// If we set it on Chrome we get this wounderful bug: #6725
9795 						if (Env.ie) {
9796 							handleElm.contentEditable = false;
9797 						}
9798 					} else {
9799 						dom.show(handleElm);
9800 					}
9801 
9802 					if (!handle.elm) {
9803 						dom.bind(handleElm, 'mousedown', function(e) {
9804 							e.stopImmediatePropagation();
9805 							e.preventDefault();
9806 							startDrag(e);
9807 						});
9808 
9809 						handle.elm = handleElm;
9810 					}
9811 
9812 					/*
9813 					var halfHandleW = handleElm.offsetWidth / 2;
9814 					var halfHandleH = handleElm.offsetHeight / 2;
9815 
9816 					// Position element
9817 					dom.setStyles(handleElm, {
9818 						left: Math.floor((targetWidth * handle[0] + selectedElmX) - halfHandleW + (handle[2] * halfHandleW)),
9819 						top: Math.floor((targetHeight * handle[1] + selectedElmY) - halfHandleH + (handle[3] * halfHandleH))
9820 					});
9821 					*/
9822 
9823 					// Position element
9824 					dom.setStyles(handleElm, {
9825 						left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
9826 						top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
9827 					});
9828 				});
9829 			} else {
9830 				hideResizeRect();
9831 			}
9832 
9833 			selectedElm.setAttribute('data-mce-selected', '1');
9834 		}
9835 
9836 		function hideResizeRect() {
9837 			var name, handleElm;
9838 
9839 			unbindResizeHandleEvents();
9840 
9841 			if (selectedElm) {
9842 				selectedElm.removeAttribute('data-mce-selected');
9843 			}
9844 
9845 			for (name in resizeHandles) {
9846 				handleElm = dom.get('mceResizeHandle' + name);
9847 				if (handleElm) {
9848 					dom.unbind(handleElm);
9849 					dom.remove(handleElm);
9850 				}
9851 			}
9852 		}
9853 
9854 		function updateResizeRect(e) {
9855 			var controlElm;
9856 
9857 			function isChildOrEqual(node, parent) {
9858 				if (node) {
9859 					do {
9860 						if (node === parent) {
9861 							return true;
9862 						}
9863 					} while ((node = node.parentNode));
9864 				}
9865 			}
9866 
9867 			// Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
9868 			each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) {
9869 				img.removeAttribute('data-mce-selected');
9870 			});
9871 
9872 			controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
9873 			controlElm = dom.getParent(controlElm, isIE ? 'table' : 'table,img,hr');
9874 
9875 			if (isChildOrEqual(controlElm, editor.getBody())) {
9876 				disableGeckoResize();
9877 
9878 				if (isChildOrEqual(selection.getStart(), controlElm) && isChildOrEqual(selection.getEnd(), controlElm)) {
9879 					if (!isIE || (controlElm != selection.getStart() && selection.getStart().nodeName !== 'IMG')) {
9880 						showResizeRect(controlElm);
9881 						return;
9882 					}
9883 				}
9884 			}
9885 
9886 			hideResizeRect();
9887 		}
9888 
9889 		function attachEvent(elm, name, func) {
9890 			if (elm && elm.attachEvent) {
9891 				elm.attachEvent('on' + name, func);
9892 			}
9893 		}
9894 
9895 		function detachEvent(elm, name, func) {
9896 			if (elm && elm.detachEvent) {
9897 				elm.detachEvent('on' + name, func);
9898 			}
9899 		}
9900 
9901 		function resizeNativeStart(e) {
9902 			var target = e.srcElement, pos, name, corner, cornerX, cornerY, relativeX, relativeY;
9903 
9904 			pos = target.getBoundingClientRect();
9905 			relativeX = lastMouseDownEvent.clientX - pos.left;
9906 			relativeY = lastMouseDownEvent.clientY - pos.top;
9907 
9908 			// Figure out what corner we are draging on
9909 			for (name in resizeHandles) {
9910 				corner = resizeHandles[name];
9911 
9912 				cornerX = target.offsetWidth * corner[0];
9913 				cornerY = target.offsetHeight * corner[1];
9914 
9915 				if (Math.abs(cornerX - relativeX) < 8 && Math.abs(cornerY - relativeY) < 8) {
9916 					selectedHandle = corner;
9917 					break;
9918 				}
9919 			}
9920 
9921 			// Remove native selection and let the magic begin
9922 			resizeStarted = true;
9923 			editor.getDoc().selection.empty();
9924 			showResizeRect(target, name, lastMouseDownEvent);
9925 		}
9926 
9927 		function nativeControlSelect(e) {
9928 			var target = e.srcElement;
9929 
9930 			if (target != selectedElm) {
9931 				detachResizeStartListener();
9932 
9933 				if (target.id.indexOf('mceResizeHandle') === 0) {
9934 					e.returnValue = false;
9935 					return;
9936 				}
9937 
9938 				if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') {
9939 					hideResizeRect();
9940 					selectedElm = target;
9941 					attachEvent(target, 'resizestart', resizeNativeStart);
9942 				}
9943 			}
9944 		}
9945 
9946 		function detachResizeStartListener() {
9947 			detachEvent(selectedElm, 'resizestart', resizeNativeStart);
9948 		}
9949 
9950 		function unbindResizeHandleEvents() {
9951 			for (var name in resizeHandles) {
9952 				var handle = resizeHandles[name];
9953 
9954 				if (handle.elm) {
9955 					dom.unbind(handle.elm);
9956 					delete handle.elm;
9957 				}
9958 			}
9959 		}
9960 
9961 		function disableGeckoResize() {
9962 			try {
9963 				// Disable object resizing on Gecko
9964 				editor.getDoc().execCommand('enableObjectResizing', false, false);
9965 			} catch (ex) {
9966 				// Ignore
9967 			}
9968 		}
9969 
9970 		function controlSelect(elm) {
9971 			var ctrlRng;
9972 
9973 			if (!isIE) {
9974 				return;
9975 			}
9976 
9977 			ctrlRng = editableDoc.body.createControlRange();
9978 
9979 			try {
9980 				ctrlRng.addElement(elm);
9981 				ctrlRng.select();
9982 				return true;
9983 			} catch (ex) {
9984 				// Ignore since the element can't be control selected for example a P tag
9985 			}
9986 		}
9987 
9988 		editor.on('init', function() {
9989 			if (isIE) {
9990 				// Hide the resize rect on resize and reselect the image
9991 				editor.on('ObjectResized', function(e) {
9992 					if (e.target.nodeName != 'TABLE') {
9993 						hideResizeRect();
9994 						controlSelect(e.target);
9995 					}
9996 				});
9997 
9998 				attachEvent(editor.getBody(), 'controlselect', nativeControlSelect);
9999 
10000 				editor.on('mousedown', function(e) {
10001 					lastMouseDownEvent = e;
10002 				});
10003 			} else {
10004 				disableGeckoResize();
10005 
10006 				if (Env.ie >= 11) {
10007 					// TODO: Drag/drop doesn't work
10008 					editor.on('mouseup', function(e) {
10009 						var nodeName = e.target.nodeName;
10010 
10011 						if (/^(TABLE|IMG|HR)$/.test(nodeName)) {
10012 							editor.selection.select(e.target, nodeName == 'TABLE');
10013 							editor.nodeChanged();
10014 						}
10015 					});
10016 
10017 					editor.dom.bind(editor.getBody(), 'mscontrolselect', function(e) {
10018 						if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) {
10019 							e.preventDefault();
10020 
10021 							// This moves the selection from being a control selection to a text like selection like in WebKit #6753
10022 							// TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections.
10023 							if (e.target.tagName == 'IMG') {
10024 								window.setTimeout(function() {
10025 									editor.selection.select(e.target);
10026 								}, 0);
10027 							}
10028 						}
10029 					});
10030 				}
10031 			}
10032 
10033 			editor.on('nodechange mousedown mouseup ResizeEditor', updateResizeRect);
10034 
10035 			// Update resize rect while typing in a table
10036 			editor.on('keydown keyup', function(e) {
10037 				if (selectedElm && selectedElm.nodeName == "TABLE") {
10038 					updateResizeRect(e);
10039 				}
10040 			});
10041 
10042 			editor.on('hide', hideResizeRect);
10043 
10044 			// Hide rect on focusout since it would float on top of windows otherwise
10045 			//editor.on('focusout', hideResizeRect);
10046 		});
10047 
10048 		editor.on('remove', unbindResizeHandleEvents);
10049 
10050 		function destroy() {
10051 			selectedElm = selectedElmGhost = null;
10052 
10053 			if (isIE) {
10054 				detachResizeStartListener();
10055 				detachEvent(editor.getBody(), 'controlselect', nativeControlSelect);
10056 			}
10057 		}
10058 
10059 		return {
10060 			isResizable: isResizable,
10061 			showResizeRect: showResizeRect,
10062 			hideResizeRect: hideResizeRect,
10063 			updateResizeRect: updateResizeRect,
10064 			controlSelect: controlSelect,
10065 			destroy: destroy
10066 		};
10067 	};
10068 });
10069 
10070 // Included from: js/tinymce/classes/dom/RangeUtils.js
10071 
10072 /**
10073  * Range.js
10074  *
10075  * Copyright, Moxiecode Systems AB
10076  * Released under LGPL License.
10077  *
10078  * License: http://www.tinymce.com/license
10079  * Contributing: http://www.tinymce.com/contributing
10080  */
10081 
10082 /**
10083  * RangeUtils
10084  *
10085  * @class tinymce.dom.RangeUtils
10086  * @private
10087  */
10088 define("tinymce/dom/RangeUtils", [
10089 	"tinymce/util/Tools",
10090 	"tinymce/dom/TreeWalker"
10091 ], function(Tools, TreeWalker) {
10092 	var each = Tools.each;
10093 
10094 	function RangeUtils(dom) {
10095 		/**
10096 		 * Walks the specified range like object and executes the callback for each sibling collection it finds.
10097 		 *
10098 		 * @method walk
10099 		 * @param {Object} rng Range like object.
10100 		 * @param {function} callback Callback function to execute for each sibling collection.
10101 		 */
10102 		this.walk = function(rng, callback) {
10103 			var startContainer = rng.startContainer,
10104 				startOffset = rng.startOffset,
10105 				endContainer = rng.endContainer,
10106 				endOffset = rng.endOffset,
10107 				ancestor, startPoint,
10108 				endPoint, node, parent, siblings, nodes;
10109 
10110 			// Handle table cell selection the table plugin enables
10111 			// you to fake select table cells and perform formatting actions on them
10112 			nodes = dom.select('td.mce-item-selected,th.mce-item-selected');
10113 			if (nodes.length > 0) {
10114 				each(nodes, function(node) {
10115 					callback([node]);
10116 				});
10117 
10118 				return;
10119 			}
10120 
10121 			/**
10122 			 * Excludes start/end text node if they are out side the range
10123 			 *
10124 			 * @private
10125 			 * @param {Array} nodes Nodes to exclude items from.
10126 			 * @return {Array} Array with nodes excluding the start/end container if needed.
10127 			 */
10128 			function exclude(nodes) {
10129 				var node;
10130 
10131 				// First node is excluded
10132 				node = nodes[0];
10133 				if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
10134 					nodes.splice(0, 1);
10135 				}
10136 
10137 				// Last node is excluded
10138 				node = nodes[nodes.length - 1];
10139 				if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
10140 					nodes.splice(nodes.length - 1, 1);
10141 				}
10142 
10143 				return nodes;
10144 			}
10145 
10146 			/**
10147 			 * Collects siblings
10148 			 *
10149 			 * @private
10150 			 * @param {Node} node Node to collect siblings from.
10151 			 * @param {String} name Name of the sibling to check for.
10152 			 * @return {Array} Array of collected siblings.
10153 			 */
10154 			function collectSiblings(node, name, end_node) {
10155 				var siblings = [];
10156 
10157 				for (; node && node != end_node; node = node[name]) {
10158 					siblings.push(node);
10159 				}
10160 
10161 				return siblings;
10162 			}
10163 
10164 			/**
10165 			 * Find an end point this is the node just before the common ancestor root.
10166 			 *
10167 			 * @private
10168 			 * @param {Node} node Node to start at.
10169 			 * @param {Node} root Root/ancestor element to stop just before.
10170 			 * @return {Node} Node just before the root element.
10171 			 */
10172 			function findEndPoint(node, root) {
10173 				do {
10174 					if (node.parentNode == root) {
10175 						return node;
10176 					}
10177 
10178 					node = node.parentNode;
10179 				} while(node);
10180 			}
10181 
10182 			function walkBoundary(start_node, end_node, next) {
10183 				var siblingName = next ? 'nextSibling' : 'previousSibling';
10184 
10185 				for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
10186 					parent = node.parentNode;
10187 					siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
10188 
10189 					if (siblings.length) {
10190 						if (!next) {
10191 							siblings.reverse();
10192 						}
10193 
10194 						callback(exclude(siblings));
10195 					}
10196 				}
10197 			}
10198 
10199 			// If index based start position then resolve it
10200 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
10201 				startContainer = startContainer.childNodes[startOffset];
10202 			}
10203 
10204 			// If index based end position then resolve it
10205 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
10206 				endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
10207 			}
10208 
10209 			// Same container
10210 			if (startContainer == endContainer) {
10211 				return callback(exclude([startContainer]));
10212 			}
10213 
10214 			// Find common ancestor and end points
10215 			ancestor = dom.findCommonAncestor(startContainer, endContainer);
10216 
10217 			// Process left side
10218 			for (node = startContainer; node; node = node.parentNode) {
10219 				if (node === endContainer) {
10220 					return walkBoundary(startContainer, ancestor, true);
10221 				}
10222 
10223 				if (node === ancestor) {
10224 					break;
10225 				}
10226 			}
10227 
10228 			// Process right side
10229 			for (node = endContainer; node; node = node.parentNode) {
10230 				if (node === startContainer) {
10231 					return walkBoundary(endContainer, ancestor);
10232 				}
10233 
10234 				if (node === ancestor) {
10235 					break;
10236 				}
10237 			}
10238 
10239 			// Find start/end point
10240 			startPoint = findEndPoint(startContainer, ancestor) || startContainer;
10241 			endPoint = findEndPoint(endContainer, ancestor) || endContainer;
10242 
10243 			// Walk left leaf
10244 			walkBoundary(startContainer, startPoint, true);
10245 
10246 			// Walk the middle from start to end point
10247 			siblings = collectSiblings(
10248 				startPoint == startContainer ? startPoint : startPoint.nextSibling,
10249 				'nextSibling',
10250 				endPoint == endContainer ? endPoint.nextSibling : endPoint
10251 			);
10252 
10253 			if (siblings.length) {
10254 				callback(exclude(siblings));
10255 			}
10256 
10257 			// Walk right leaf
10258 			walkBoundary(endContainer, endPoint);
10259 		};
10260 
10261 		/**
10262 		 * Splits the specified range at it's start/end points.
10263 		 *
10264 		 * @private
10265 		 * @param {Range/RangeObject} rng Range to split.
10266 		 * @return {Object} Range position object.
10267 		 */
10268 		this.split = function(rng) {
10269 			var startContainer = rng.startContainer,
10270 				startOffset = rng.startOffset,
10271 				endContainer = rng.endContainer,
10272 				endOffset = rng.endOffset;
10273 
10274 			function splitText(node, offset) {
10275 				return node.splitText(offset);
10276 			}
10277 
10278 			// Handle single text node
10279 			if (startContainer == endContainer && startContainer.nodeType == 3) {
10280 				if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
10281 					endContainer = splitText(startContainer, startOffset);
10282 					startContainer = endContainer.previousSibling;
10283 
10284 					if (endOffset > startOffset) {
10285 						endOffset = endOffset - startOffset;
10286 						startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
10287 						endOffset = endContainer.nodeValue.length;
10288 						startOffset = 0;
10289 					} else {
10290 						endOffset = 0;
10291 					}
10292 				}
10293 			} else {
10294 				// Split startContainer text node if needed
10295 				if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
10296 					startContainer = splitText(startContainer, startOffset);
10297 					startOffset = 0;
10298 				}
10299 
10300 				// Split endContainer text node if needed
10301 				if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
10302 					endContainer = splitText(endContainer, endOffset).previousSibling;
10303 					endOffset = endContainer.nodeValue.length;
10304 				}
10305 			}
10306 
10307 			return {
10308 				startContainer: startContainer,
10309 				startOffset: startOffset,
10310 				endContainer: endContainer,
10311 				endOffset: endOffset
10312 			};
10313 		};
10314 
10315 		/**
10316 		 * Normalizes the specified range by finding the closest best suitable caret location.
10317 		 *
10318 		 * @private
10319 		 * @param {Range} rng Range to normalize.
10320 		 * @return {Boolean} True/false if the specified range was normalized or not.
10321 		 */
10322 		this.normalize = function(rng) {
10323 			var normalized, collapsed;
10324 
10325 			function normalizeEndPoint(start) {
10326 				var container, offset, walker, body = dom.getRoot(), node, nonEmptyElementsMap;
10327 				var directionLeft, isAfterNode;
10328 
10329 				function hasBrBeforeAfter(node, left) {
10330 					var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
10331 
10332 					while ((node = walker[left ? 'prev' : 'next']())) {
10333 						if (node.nodeName === "BR") {
10334 							return true;
10335 						}
10336 					}
10337 				}
10338 
10339 				function isPrevNode(node, name) {
10340 					return node.previousSibling && node.previousSibling.nodeName == name;
10341 				}
10342 
10343 				// Walks the dom left/right to find a suitable text node to move the endpoint into
10344 				// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
10345 				function findTextNodeRelative(left, startNode) {
10346 					var walker, lastInlineElement, parentBlockContainer;
10347 
10348 					startNode = startNode || container;
10349 					parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body;
10350 
10351 					// Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680
10352 					// This: <p><br>|</p> becomes <p>|<br></p>
10353 					if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) {
10354 						container = startNode.parentNode;
10355 						offset = dom.nodeIndex(startNode);
10356 						normalized = true;
10357 						return;
10358 					}
10359 
10360 					// Walk left until we hit a text node we can move to or a block/br/img
10361 					walker = new TreeWalker(startNode, parentBlockContainer);
10362 					while ((node = walker[left ? 'prev' : 'next']())) {
10363 						// Break if we hit a non content editable node
10364 						if (dom.getContentEditableParent(node) === "false") {
10365 							return;
10366 						}
10367 
10368 						// Found text node that has a length
10369 						if (node.nodeType === 3 && node.nodeValue.length > 0) {
10370 							container = node;
10371 							offset = left ? node.nodeValue.length : 0;
10372 							normalized = true;
10373 							return;
10374 						}
10375 
10376 						// Break if we find a block or a BR/IMG/INPUT etc
10377 						if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
10378 							return;
10379 						}
10380 
10381 						lastInlineElement = node;
10382 					}
10383 
10384 					// Only fetch the last inline element when in caret mode for now
10385 					if (collapsed && lastInlineElement) {
10386 						container = lastInlineElement;
10387 						normalized = true;
10388 						offset = 0;
10389 					}
10390 				}
10391 
10392 				container = rng[(start ? 'start' : 'end') + 'Container'];
10393 				offset = rng[(start ? 'start' : 'end') + 'Offset'];
10394 				isAfterNode = container.nodeType == 1 && offset === container.childNodes.length;
10395 				nonEmptyElementsMap = dom.schema.getNonEmptyElements();
10396 				directionLeft = start;
10397 
10398 				if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
10399 					directionLeft = false;
10400 				}
10401 
10402 				// If the container is a document move it to the body element
10403 				if (container.nodeType === 9) {
10404 					container = dom.getRoot();
10405 					offset = 0;
10406 				}
10407 
10408 				// If the container is body try move it into the closest text node or position
10409 				if (container === body) {
10410 					// If start is before/after a image, table etc
10411 					if (directionLeft) {
10412 						node = container.childNodes[offset > 0 ? offset - 1 : 0];
10413 						if (node) {
10414 							if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
10415 								return;
10416 							}
10417 						}
10418 					}
10419 
10420 					// Resolve the index
10421 					if (container.hasChildNodes()) {
10422 						offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
10423 						container = container.childNodes[offset];
10424 						offset = 0;
10425 
10426 						// Don't walk into elements that doesn't have any child nodes like a IMG
10427 						if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
10428 							// Walk the DOM to find a text node to place the caret at or a BR
10429 							node = container;
10430 							walker = new TreeWalker(container, body);
10431 
10432 							do {
10433 								// Found a text node use that position
10434 								if (node.nodeType === 3 && node.nodeValue.length > 0) {
10435 									offset = directionLeft ? 0 : node.nodeValue.length;
10436 									container = node;
10437 									normalized = true;
10438 									break;
10439 								}
10440 
10441 								// Found a BR/IMG element that we can place the caret before
10442 								if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
10443 									offset = dom.nodeIndex(node);
10444 									container = node.parentNode;
10445 
10446 									// Put caret after image when moving the end point
10447 									if (node.nodeName ==  "IMG" && !directionLeft) {
10448 										offset++;
10449 									}
10450 
10451 									normalized = true;
10452 									break;
10453 								}
10454 							} while ((node = (directionLeft ? walker.next() : walker.prev())));
10455 						}
10456 					}
10457 				}
10458 
10459 				// Lean the caret to the left if possible
10460 				if (collapsed) {
10461 					// So this: <b>x</b><i>|x</i>
10462 					// Becomes: <b>x|</b><i>x</i>
10463 					// Seems that only gecko has issues with this
10464 					if (container.nodeType === 3 && offset === 0) {
10465 						findTextNodeRelative(true);
10466 					}
10467 
10468 					// Lean left into empty inline elements when the caret is before a BR
10469 					// So this: <i><b></b><i>|<br></i>
10470 					// Becomes: <i><b>|</b><i><br></i>
10471 					// Seems that only gecko has issues with this.
10472 					// Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p>
10473 					if (container.nodeType === 1) {
10474 						node = container.childNodes[offset];
10475 
10476 						// Offset is after the containers last child
10477 						// then use the previous child for normalization
10478 						if (!node) {
10479 							node = container.childNodes[offset - 1];
10480 						}
10481 
10482 						if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') &&
10483 							!hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
10484 							findTextNodeRelative(true, node);
10485 						}
10486 					}
10487 				}
10488 
10489 				// Lean the start of the selection right if possible
10490 				// So this: x[<b>x]</b>
10491 				// Becomes: x<b>[x]</b>
10492 				if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
10493 					findTextNodeRelative(false);
10494 				}
10495 
10496 				// Set endpoint if it was normalized
10497 				if (normalized) {
10498 					rng['set' + (start ? 'Start' : 'End')](container, offset);
10499 				}
10500 			}
10501 
10502 			collapsed = rng.collapsed;
10503 
10504 			normalizeEndPoint(true);
10505 
10506 			if (!collapsed) {
10507 				normalizeEndPoint();
10508 			}
10509 
10510 			// If it was collapsed then make sure it still is
10511 			if (normalized && collapsed) {
10512 				rng.collapse(true);
10513 			}
10514 
10515 			return normalized;
10516 		};
10517 	}
10518 
10519 	/**
10520 	 * Compares two ranges and checks if they are equal.
10521 	 *
10522 	 * @static
10523 	 * @method compareRanges
10524 	 * @param {DOMRange} rng1 First range to compare.
10525 	 * @param {DOMRange} rng2 First range to compare.
10526 	 * @return {Boolean} true/false if the ranges are equal.
10527 	 */
10528 	RangeUtils.compareRanges = function(rng1, rng2) {
10529 		if (rng1 && rng2) {
10530 			// Compare native IE ranges
10531 			if (rng1.item || rng1.duplicate) {
10532 				// Both are control ranges and the selected element matches
10533 				if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) {
10534 					return true;
10535 				}
10536 
10537 				// Both are text ranges and the range matches
10538 				if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
10539 					return true;
10540 				}
10541 			} else {
10542 				// Compare w3c ranges
10543 				return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
10544 			}
10545 		}
10546 
10547 		return false;
10548 	};
10549 
10550 	return RangeUtils;
10551 });
10552 
10553 // Included from: js/tinymce/classes/dom/Selection.js
10554 
10555 /**
10556  * Selection.js
10557  *
10558  * Copyright, Moxiecode Systems AB
10559  * Released under LGPL License.
10560  *
10561  * License: http://www.tinymce.com/license
10562  * Contributing: http://www.tinymce.com/contributing
10563  */
10564 
10565 /**
10566  * This class handles text and control selection it's an crossbrowser utility class.
10567  * Consult the TinyMCE Wiki API for more details and examples on how to use this class.
10568  *
10569  * @class tinymce.dom.Selection
10570  * @example
10571  * // Getting the currently selected node for the active editor
10572  * alert(tinymce.activeEditor.selection.getNode().nodeName);
10573  */
10574 define("tinymce/dom/Selection", [
10575 	"tinymce/dom/TreeWalker",
10576 	"tinymce/dom/TridentSelection",
10577 	"tinymce/dom/ControlSelection",
10578 	"tinymce/dom/RangeUtils",
10579 	"tinymce/Env",
10580 	"tinymce/util/Tools"
10581 ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, Env, Tools) {
10582 	var each = Tools.each, grep = Tools.grep, trim = Tools.trim;
10583 	var isIE = Env.ie, isOpera = Env.opera;
10584 
10585 	/**
10586 	 * Constructs a new selection instance.
10587 	 *
10588 	 * @constructor
10589 	 * @method Selection
10590 	 * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference.
10591 	 * @param {Window} win Window to bind the selection object to.
10592 	 * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent.
10593 	 */
10594 	function Selection(dom, win, serializer, editor) {
10595 		var self = this;
10596 
10597 		self.dom = dom;
10598 		self.win = win;
10599 		self.serializer = serializer;
10600 		self.editor = editor;
10601 
10602 		self.controlSelection = new ControlSelection(self, editor);
10603 
10604 		// No W3C Range support
10605 		if (!self.win.getSelection) {
10606 			self.tridentSel = new TridentSelection(self);
10607 		}
10608 	}
10609 
10610 	Selection.prototype = {
10611 		/**
10612 		 * Move the selection cursor range to the specified node and offset.
10613 		 * If there is no node specified it will move it to the first suitable location within the body.
10614 		 *
10615 		 * @method setCursorLocation
10616 		 * @param {Node} node Optional node to put the cursor in.
10617 		 * @param {Number} offset Optional offset from the start of the node to put the cursor at.
10618 		 */
10619 		setCursorLocation: function(node, offset) {
10620 			var self = this, rng = self.dom.createRng();
10621 
10622 			if (!node) {
10623 				self._moveEndPoint(rng, self.editor.getBody(), true);
10624 				self.setRng(rng);
10625 			} else {
10626 				rng.setStart(node, offset);
10627 				rng.setEnd(node, offset);
10628 				self.setRng(rng);
10629 				self.collapse(false);
10630 			}
10631 		},
10632 
10633 		/**
10634 		 * Returns the selected contents using the DOM serializer passed in to this class.
10635 		 *
10636 		 * @method getContent
10637 		 * @param {Object} s Optional settings class with for example output format text or html.
10638 		 * @return {String} Selected contents in for example HTML format.
10639 		 * @example
10640 		 * // Alerts the currently selected contents
10641 		 * alert(tinymce.activeEditor.selection.getContent());
10642 		 *
10643 		 * // Alerts the currently selected contents as plain text
10644 		 * alert(tinymce.activeEditor.selection.getContent({format: 'text'}));
10645 		 */
10646 		getContent: function(args) {
10647 			var self = this, rng = self.getRng(), tmpElm = self.dom.create("body");
10648 			var se = self.getSel(), whiteSpaceBefore, whiteSpaceAfter, fragment;
10649 
10650 			args = args || {};
10651 			whiteSpaceBefore = whiteSpaceAfter = '';
10652 			args.get = true;
10653 			args.format = args.format || 'html';
10654 			args.selection = true;
10655 			self.editor.fire('BeforeGetContent', args);
10656 
10657 			if (args.format == 'text') {
10658 				return self.isCollapsed() ? '' : (rng.text || (se.toString ? se.toString() : ''));
10659 			}
10660 
10661 			if (rng.cloneContents) {
10662 				fragment = rng.cloneContents();
10663 
10664 				if (fragment) {
10665 					tmpElm.appendChild(fragment);
10666 				}
10667 			} else if (rng.item !== undefined || rng.htmlText !== undefined) {
10668 				// IE will produce invalid markup if elements are present that
10669 				// it doesn't understand like custom elements or HTML5 elements.
10670 				// Adding a BR in front of the contents and then remoiving it seems to fix it though.
10671 				tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText);
10672 				tmpElm.removeChild(tmpElm.firstChild);
10673 			} else {
10674 				tmpElm.innerHTML = rng.toString();
10675 			}
10676 
10677 			// Keep whitespace before and after
10678 			if (/^\s/.test(tmpElm.innerHTML)) {
10679 				whiteSpaceBefore = ' ';
10680 			}
10681 
10682 			if (/\s+$/.test(tmpElm.innerHTML)) {
10683 				whiteSpaceAfter = ' ';
10684 			}
10685 
10686 			args.getInner = true;
10687 
10688 			args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter;
10689 			self.editor.fire('GetContent', args);
10690 
10691 			return args.content;
10692 		},
10693 
10694 		/**
10695 		 * Sets the current selection to the specified content. If any contents is selected it will be replaced
10696 		 * with the contents passed in to this function. If there is no selection the contents will be inserted
10697 		 * where the caret is placed in the editor/page.
10698 		 *
10699 		 * @method setContent
10700 		 * @param {String} content HTML contents to set could also be other formats depending on settings.
10701 		 * @param {Object} args Optional settings object with for example data format.
10702 		 * @example
10703 		 * // Inserts some HTML contents at the current selection
10704 		 * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>');
10705 		 */
10706 		setContent: function(content, args) {
10707 			var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
10708 
10709 			args = args || {format: 'html'};
10710 			args.set = true;
10711 			args.selection = true;
10712 			content = args.content = content;
10713 
10714 			// Dispatch before set content event
10715 			if (!args.no_events) {
10716 				self.editor.fire('BeforeSetContent', args);
10717 			}
10718 
10719 			content = args.content;
10720 
10721 			if (rng.insertNode) {
10722 				// Make caret marker since insertNode places the caret in the beginning of text after insert
10723 				content += '<span id="__caret">_</span>';
10724 
10725 				// Delete and insert new node
10726 				if (rng.startContainer == doc && rng.endContainer == doc) {
10727 					// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
10728 					doc.body.innerHTML = content;
10729 				} else {
10730 					rng.deleteContents();
10731 
10732 					if (doc.body.childNodes.length === 0) {
10733 						doc.body.innerHTML = content;
10734 					} else {
10735 						// createContextualFragment doesn't exists in IE 9 DOMRanges
10736 						if (rng.createContextualFragment) {
10737 							rng.insertNode(rng.createContextualFragment(content));
10738 						} else {
10739 							// Fake createContextualFragment call in IE 9
10740 							frag = doc.createDocumentFragment();
10741 							temp = doc.createElement('div');
10742 
10743 							frag.appendChild(temp);
10744 							temp.outerHTML = content;
10745 
10746 							rng.insertNode(frag);
10747 						}
10748 					}
10749 				}
10750 
10751 				// Move to caret marker
10752 				caretNode = self.dom.get('__caret');
10753 
10754 				// Make sure we wrap it compleatly, Opera fails with a simple select call
10755 				rng = doc.createRange();
10756 				rng.setStartBefore(caretNode);
10757 				rng.setEndBefore(caretNode);
10758 				self.setRng(rng);
10759 
10760 				// Remove the caret position
10761 				self.dom.remove('__caret');
10762 
10763 				try {
10764 					self.setRng(rng);
10765 				} catch (ex) {
10766 					// Might fail on Opera for some odd reason
10767 				}
10768 			} else {
10769 				if (rng.item) {
10770 					// Delete content and get caret text selection
10771 					doc.execCommand('Delete', false, null);
10772 					rng = self.getRng();
10773 				}
10774 
10775 				// Explorer removes spaces from the beginning of pasted contents
10776 				if (/^\s+/.test(content)) {
10777 					rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
10778 					self.dom.remove('__mce_tmp');
10779 				} else {
10780 					rng.pasteHTML(content);
10781 				}
10782 			}
10783 
10784 			// Dispatch set content event
10785 			if (!args.no_events) {
10786 				self.editor.fire('SetContent', args);
10787 			}
10788 		},
10789 
10790 		/**
10791 		 * Returns the start element of a selection range. If the start is in a text
10792 		 * node the parent element will be returned.
10793 		 *
10794 		 * @method getStart
10795 		 * @return {Element} Start element of selection range.
10796 		 */
10797 		getStart: function() {
10798 			var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
10799 
10800 			if (rng.duplicate || rng.item) {
10801 				// Control selection, return first item
10802 				if (rng.item) {
10803 					return rng.item(0);
10804 				}
10805 
10806 				// Get start element
10807 				checkRng = rng.duplicate();
10808 				checkRng.collapse(1);
10809 				startElement = checkRng.parentElement();
10810 				if (startElement.ownerDocument !== self.dom.doc) {
10811 					startElement = self.dom.getRoot();
10812 				}
10813 
10814 				// Check if range parent is inside the start element, then return the inner parent element
10815 				// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
10816 				parentElement = node = rng.parentElement();
10817 				while ((node = node.parentNode)) {
10818 					if (node == startElement) {
10819 						startElement = parentElement;
10820 						break;
10821 					}
10822 				}
10823 
10824 				return startElement;
10825 			} else {
10826 				startElement = rng.startContainer;
10827 
10828 				if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
10829 					startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
10830 				}
10831 
10832 				if (startElement && startElement.nodeType == 3) {
10833 					return startElement.parentNode;
10834 				}
10835 
10836 				return startElement;
10837 			}
10838 		},
10839 
10840 		/**
10841 		 * Returns the end element of a selection range. If the end is in a text
10842 		 * node the parent element will be returned.
10843 		 *
10844 		 * @method getEnd
10845 		 * @return {Element} End element of selection range.
10846 		 */
10847 		getEnd: function() {
10848 			var self = this, rng = self.getRng(), endElement, endOffset;
10849 
10850 			if (rng.duplicate || rng.item) {
10851 				if (rng.item) {
10852 					return rng.item(0);
10853 				}
10854 
10855 				rng = rng.duplicate();
10856 				rng.collapse(0);
10857 				endElement = rng.parentElement();
10858 				if (endElement.ownerDocument !== self.dom.doc) {
10859 					endElement = self.dom.getRoot();
10860 				}
10861 
10862 				if (endElement && endElement.nodeName == 'BODY') {
10863 					return endElement.lastChild || endElement;
10864 				}
10865 
10866 				return endElement;
10867 			} else {
10868 				endElement = rng.endContainer;
10869 				endOffset = rng.endOffset;
10870 
10871 				if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
10872 					endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
10873 				}
10874 
10875 				if (endElement && endElement.nodeType == 3) {
10876 					return endElement.parentNode;
10877 				}
10878 
10879 				return endElement;
10880 			}
10881 		},
10882 
10883 		/**
10884 		 * Returns a bookmark location for the current selection. This bookmark object
10885 		 * can then be used to restore the selection after some content modification to the document.
10886 		 *
10887 		 * @method getBookmark
10888 		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
10889 		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
10890 		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
10891 		 * @example
10892 		 * // Stores a bookmark of the current selection
10893 		 * var bm = tinymce.activeEditor.selection.getBookmark();
10894 		 *
10895 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
10896 		 *
10897 		 * // Restore the selection bookmark
10898 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
10899 		 */
10900 		getBookmark: function(type, normalized) {
10901 			var self = this, dom = self.dom, rng, rng2, id, collapsed, name, element, chr = '', styles;
10902 
10903 			function findIndex(name, element) {
10904 				var index = 0;
10905 
10906 				each(dom.select(name), function(node, i) {
10907 					if (node == element) {
10908 						index = i;
10909 					}
10910 				});
10911 
10912 				return index;
10913 			}
10914 
10915 			function normalizeTableCellSelection(rng) {
10916 				function moveEndPoint(start) {
10917 					var container, offset, childNodes, prefix = start ? 'start' : 'end';
10918 
10919 					container = rng[prefix + 'Container'];
10920 					offset = rng[prefix + 'Offset'];
10921 
10922 					if (container.nodeType == 1 && container.nodeName == "TR") {
10923 						childNodes = container.childNodes;
10924 						container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
10925 						if (container) {
10926 							offset = start ? 0 : container.childNodes.length;
10927 							rng['set' + (start ? 'Start' : 'End')](container, offset);
10928 						}
10929 					}
10930 				}
10931 
10932 				moveEndPoint(true);
10933 				moveEndPoint();
10934 
10935 				return rng;
10936 			}
10937 
10938 			function getLocation() {
10939 				var rng = self.getRng(true), root = dom.getRoot(), bookmark = {};
10940 
10941 				function getPoint(rng, start) {
10942 					var container = rng[start ? 'startContainer' : 'endContainer'],
10943 						offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
10944 
10945 					if (container.nodeType == 3) {
10946 						if (normalized) {
10947 							for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
10948 								offset += node.nodeValue.length;
10949 							}
10950 						}
10951 
10952 						point.push(offset);
10953 					} else {
10954 						childNodes = container.childNodes;
10955 
10956 						if (offset >= childNodes.length && childNodes.length) {
10957 							after = 1;
10958 							offset = Math.max(0, childNodes.length - 1);
10959 						}
10960 
10961 						point.push(self.dom.nodeIndex(childNodes[offset], normalized) + after);
10962 					}
10963 
10964 					for (; container && container != root; container = container.parentNode) {
10965 						point.push(self.dom.nodeIndex(container, normalized));
10966 					}
10967 
10968 					return point;
10969 				}
10970 
10971 				bookmark.start = getPoint(rng, true);
10972 
10973 				if (!self.isCollapsed()) {
10974 					bookmark.end = getPoint(rng);
10975 				}
10976 
10977 				return bookmark;
10978 			}
10979 
10980 			if (type == 2) {
10981 				element = self.getNode();
10982 				name = element ? element.nodeName : null;
10983 
10984 				if (name == 'IMG') {
10985 					return {name: name, index: findIndex(name, element)};
10986 				}
10987 
10988 				if (self.tridentSel) {
10989 					return self.tridentSel.getBookmark(type);
10990 				}
10991 
10992 				return getLocation();
10993 			}
10994 
10995 			// Handle simple range
10996 			if (type) {
10997 				return {rng: self.getRng()};
10998 			}
10999 
11000 			rng = self.getRng();
11001 			id = dom.uniqueId();
11002 			collapsed = self.isCollapsed();
11003 			styles = 'overflow:hidden;line-height:0px';
11004 
11005 			// Explorer method
11006 			if (rng.duplicate || rng.item) {
11007 				// Text selection
11008 				if (!rng.item) {
11009 					rng2 = rng.duplicate();
11010 
11011 					try {
11012 						// Insert start marker
11013 						rng.collapse();
11014 						rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
11015 
11016 						// Insert end marker
11017 						if (!collapsed) {
11018 							rng2.collapse(false);
11019 
11020 							// Detect the empty space after block elements in IE and move the
11021 							// end back one character <p></p>] becomes <p>]</p>
11022 							rng.moveToElementText(rng2.parentElement());
11023 							if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
11024 								rng2.move('character', -1);
11025 							}
11026 
11027 							rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
11028 						}
11029 					} catch (ex) {
11030 						// IE might throw unspecified error so lets ignore it
11031 						return null;
11032 					}
11033 				} else {
11034 					// Control selection
11035 					element = rng.item(0);
11036 					name = element.nodeName;
11037 
11038 					return {name: name, index: findIndex(name, element)};
11039 				}
11040 			} else {
11041 				element = self.getNode();
11042 				name = element.nodeName;
11043 				if (name == 'IMG') {
11044 					return {name: name, index: findIndex(name, element)};
11045 				}
11046 
11047 				// W3C method
11048 				rng2 = normalizeTableCellSelection(rng.cloneRange());
11049 
11050 				// Insert end marker
11051 				if (!collapsed) {
11052 					rng2.collapse(false);
11053 					rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
11054 				}
11055 
11056 				rng = normalizeTableCellSelection(rng);
11057 				rng.collapse(true);
11058 				rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
11059 			}
11060 
11061 			self.moveToBookmark({id: id, keep: 1});
11062 
11063 			return {id: id};
11064 		},
11065 
11066 		/**
11067 		 * Restores the selection to the specified bookmark.
11068 		 *
11069 		 * @method moveToBookmark
11070 		 * @param {Object} bookmark Bookmark to restore selection from.
11071 		 * @return {Boolean} true/false if it was successful or not.
11072 		 * @example
11073 		 * // Stores a bookmark of the current selection
11074 		 * var bm = tinymce.activeEditor.selection.getBookmark();
11075 		 *
11076 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
11077 		 *
11078 		 * // Restore the selection bookmark
11079 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
11080 		 */
11081 		moveToBookmark: function(bookmark) {
11082 			var self = this, dom = self.dom, rng, root, startContainer, endContainer, startOffset, endOffset;
11083 
11084 			function setEndPoint(start) {
11085 				var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
11086 
11087 				if (point) {
11088 					offset = point[0];
11089 
11090 					// Find container node
11091 					for (node = root, i = point.length - 1; i >= 1; i--) {
11092 						children = node.childNodes;
11093 
11094 						if (point[i] > children.length - 1) {
11095 							return;
11096 						}
11097 
11098 						node = children[point[i]];
11099 					}
11100 
11101 					// Move text offset to best suitable location
11102 					if (node.nodeType === 3) {
11103 						offset = Math.min(point[0], node.nodeValue.length);
11104 					}
11105 
11106 					// Move element offset to best suitable location
11107 					if (node.nodeType === 1) {
11108 						offset = Math.min(point[0], node.childNodes.length);
11109 					}
11110 
11111 					// Set offset within container node
11112 					if (start) {
11113 						rng.setStart(node, offset);
11114 					} else {
11115 						rng.setEnd(node, offset);
11116 					}
11117 				}
11118 
11119 				return true;
11120 			}
11121 
11122 			function restoreEndPoint(suffix) {
11123 				var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
11124 
11125 				if (marker) {
11126 					node = marker.parentNode;
11127 
11128 					if (suffix == 'start') {
11129 						if (!keep) {
11130 							idx = dom.nodeIndex(marker);
11131 						} else {
11132 							node = marker.firstChild;
11133 							idx = 1;
11134 						}
11135 
11136 						startContainer = endContainer = node;
11137 						startOffset = endOffset = idx;
11138 					} else {
11139 						if (!keep) {
11140 							idx = dom.nodeIndex(marker);
11141 						} else {
11142 							node = marker.firstChild;
11143 							idx = 1;
11144 						}
11145 
11146 						endContainer = node;
11147 						endOffset = idx;
11148 					}
11149 
11150 					if (!keep) {
11151 						prev = marker.previousSibling;
11152 						next = marker.nextSibling;
11153 
11154 						// Remove all marker text nodes
11155 						each(grep(marker.childNodes), function(node) {
11156 							if (node.nodeType == 3) {
11157 								node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
11158 							}
11159 						});
11160 
11161 						// Remove marker but keep children if for example contents where inserted into the marker
11162 						// Also remove duplicated instances of the marker for example by a
11163 						// split operation or by WebKit auto split on paste feature
11164 						while ((marker = dom.get(bookmark.id + '_' + suffix))) {
11165 							dom.remove(marker, 1);
11166 						}
11167 
11168 						// If siblings are text nodes then merge them unless it's Opera since it some how removes the node
11169 						// and we are sniffing since adding a lot of detection code for a browser with 3% of the market
11170 						// isn't worth the effort. Sorry, Opera but it's just a fact
11171 						if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !isOpera) {
11172 							idx = prev.nodeValue.length;
11173 							prev.appendData(next.nodeValue);
11174 							dom.remove(next);
11175 
11176 							if (suffix == 'start') {
11177 								startContainer = endContainer = prev;
11178 								startOffset = endOffset = idx;
11179 							} else {
11180 								endContainer = prev;
11181 								endOffset = idx;
11182 							}
11183 						}
11184 					}
11185 				}
11186 			}
11187 
11188 			function addBogus(node) {
11189 				// Adds a bogus BR element for empty block elements
11190 				if (dom.isBlock(node) && !node.innerHTML && !isIE) {
11191 					node.innerHTML = '<br data-mce-bogus="1" />';
11192 				}
11193 
11194 				return node;
11195 			}
11196 
11197 			if (bookmark) {
11198 				if (bookmark.start) {
11199 					rng = dom.createRng();
11200 					root = dom.getRoot();
11201 
11202 					if (self.tridentSel) {
11203 						return self.tridentSel.moveToBookmark(bookmark);
11204 					}
11205 
11206 					if (setEndPoint(true) && setEndPoint()) {
11207 						self.setRng(rng);
11208 					}
11209 				} else if (bookmark.id) {
11210 					// Restore start/end points
11211 					restoreEndPoint('start');
11212 					restoreEndPoint('end');
11213 
11214 					if (startContainer) {
11215 						rng = dom.createRng();
11216 						rng.setStart(addBogus(startContainer), startOffset);
11217 						rng.setEnd(addBogus(endContainer), endOffset);
11218 						self.setRng(rng);
11219 					}
11220 				} else if (bookmark.name) {
11221 					self.select(dom.select(bookmark.name)[bookmark.index]);
11222 				} else if (bookmark.rng) {
11223 					self.setRng(bookmark.rng);
11224 				}
11225 			}
11226 		},
11227 
11228 		/**
11229 		 * Selects the specified element. This will place the start and end of the selection range around the element.
11230 		 *
11231 		 * @method select
11232 		 * @param {Element} node HMTL DOM element to select.
11233 		 * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser.
11234 		 * @return {Element} Selected element the same element as the one that got passed in.
11235 		 * @example
11236 		 * // Select the first paragraph in the active editor
11237 		 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
11238 		 */
11239 		select: function(node, content) {
11240 			var self = this, dom = self.dom, rng = dom.createRng(), idx;
11241 
11242 			// Clear stored range set by FocusManager
11243 			self.lastFocusBookmark = null;
11244 
11245 			if (node) {
11246 				if (!content && self.controlSelection.controlSelect(node)) {
11247 					return;
11248 				}
11249 
11250 				idx = dom.nodeIndex(node);
11251 				rng.setStart(node.parentNode, idx);
11252 				rng.setEnd(node.parentNode, idx + 1);
11253 
11254 				// Find first/last text node or BR element
11255 				if (content) {
11256 					self._moveEndPoint(rng, node, true);
11257 					self._moveEndPoint(rng, node);
11258 				}
11259 
11260 				self.setRng(rng);
11261 			}
11262 
11263 			return node;
11264 		},
11265 
11266 		/**
11267 		 * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection.
11268 		 *
11269 		 * @method isCollapsed
11270 		 * @return {Boolean} true/false state if the selection range is collapsed or not.
11271 		 * Collapsed means if it's a caret or a larger selection.
11272 		 */
11273 		isCollapsed: function() {
11274 			var self = this, rng = self.getRng(), sel = self.getSel();
11275 
11276 			if (!rng || rng.item) {
11277 				return false;
11278 			}
11279 
11280 			if (rng.compareEndPoints) {
11281 				return rng.compareEndPoints('StartToEnd', rng) === 0;
11282 			}
11283 
11284 			return !sel || rng.collapsed;
11285 		},
11286 
11287 		/**
11288 		 * Collapse the selection to start or end of range.
11289 		 *
11290 		 * @method collapse
11291 		 * @param {Boolean} to_start Optional boolean state if to collapse to end or not. Defaults to start.
11292 		 */
11293 		collapse: function(to_start) {
11294 			var self = this, rng = self.getRng(), node;
11295 
11296 			// Control range on IE
11297 			if (rng.item) {
11298 				node = rng.item(0);
11299 				rng = self.win.document.body.createTextRange();
11300 				rng.moveToElementText(node);
11301 			}
11302 
11303 			rng.collapse(!!to_start);
11304 			self.setRng(rng);
11305 		},
11306 
11307 		/**
11308 		 * Returns the browsers internal selection object.
11309 		 *
11310 		 * @method getSel
11311 		 * @return {Selection} Internal browser selection object.
11312 		 */
11313 		getSel: function() {
11314 			var win = this.win;
11315 
11316 			return win.getSelection ? win.getSelection() : win.document.selection;
11317 		},
11318 
11319 		/**
11320 		 * Returns the browsers internal range object.
11321 		 *
11322 		 * @method getRng
11323 		 * @param {Boolean} w3c Forces a compatible W3C range on IE.
11324 		 * @return {Range} Internal browser range object.
11325 		 * @see http://www.quirksmode.org/dom/range_intro.html
11326 		 * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/
11327 		 */
11328 		getRng: function(w3c) {
11329 			var self = this, selection, rng, elm, doc = self.win.document, ieRng;
11330 
11331 			function tryCompareBounderyPoints(how, sourceRange, destinationRange) {
11332 				try {
11333 					return sourceRange.compareBoundaryPoints(how, destinationRange);
11334 				} catch (ex) {
11335 					// Gecko throws wrong document exception if the range points
11336 					// to nodes that where removed from the dom #6690
11337 					// Browsers should mutate existing DOMRange instances so that they always point
11338 					// to something in the document this is not the case in Gecko works fine in IE/WebKit/Blink
11339 					// For performance reasons just return -1
11340 					return -1;
11341 				}
11342 			}
11343 
11344 			// Use last rng passed from FocusManager if it's available this enables
11345 			// calls to editor.selection.getStart() to work when caret focus is lost on IE
11346 			if (!w3c && self.lastFocusBookmark) {
11347 				var bookmark = self.lastFocusBookmark;
11348 
11349 				// Convert bookmark to range IE 11 fix
11350 				if (bookmark.startContainer) {
11351 					rng = doc.createRange();
11352 					rng.setStart(bookmark.startContainer, bookmark.startOffset);
11353 					rng.setEnd(bookmark.endContainer, bookmark.endOffset);
11354 				} else {
11355 					rng = bookmark;
11356 				}
11357 
11358 				return rng;
11359 			}
11360 
11361 			// Found tridentSel object then we need to use that one
11362 			if (w3c && self.tridentSel) {
11363 				return self.tridentSel.getRangeAt(0);
11364 			}
11365 
11366 			try {
11367 				if ((selection = self.getSel())) {
11368 					if (selection.rangeCount > 0) {
11369 						rng = selection.getRangeAt(0);
11370 					} else {
11371 						rng = selection.createRange ? selection.createRange() : doc.createRange();
11372 					}
11373 				}
11374 			} catch (ex) {
11375 				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
11376 			}
11377 
11378 			// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
11379 			// IE 11 doesn't support the selection object so we check for that as well
11380 			if (isIE && rng && rng.setStart && doc.selection) {
11381 				try {
11382 					// IE will sometimes throw an exception here
11383 					ieRng = doc.selection.createRange();
11384 				} catch (ex) {
11385 
11386 				}
11387 
11388 				if (ieRng && ieRng.item) {
11389 					elm = ieRng.item(0);
11390 					rng = doc.createRange();
11391 					rng.setStartBefore(elm);
11392 					rng.setEndAfter(elm);
11393 				}
11394 			}
11395 
11396 			// No range found then create an empty one
11397 			// This can occur when the editor is placed in a hidden container element on Gecko
11398 			// Or on IE when there was an exception
11399 			if (!rng) {
11400 				rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
11401 			}
11402 
11403 			// If range is at start of document then move it to start of body
11404 			if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
11405 				elm = self.dom.getRoot();
11406 				rng.setStart(elm, 0);
11407 				rng.setEnd(elm, 0);
11408 			}
11409 
11410 			if (self.selectedRange && self.explicitRange) {
11411 				if (tryCompareBounderyPoints(rng.START_TO_START, rng, self.selectedRange) === 0 &&
11412 					tryCompareBounderyPoints(rng.END_TO_END, rng, self.selectedRange) === 0) {
11413 					// Safari, Opera and Chrome only ever select text which causes the range to change.
11414 					// This lets us use the originally set range if the selection hasn't been changed by the user.
11415 					rng = self.explicitRange;
11416 				} else {
11417 					self.selectedRange = null;
11418 					self.explicitRange = null;
11419 				}
11420 			}
11421 
11422 			return rng;
11423 		},
11424 
11425 		/**
11426 		 * Changes the selection to the specified DOM range.
11427 		 *
11428 		 * @method setRng
11429 		 * @param {Range} rng Range to select.
11430 		 */
11431 		setRng: function(rng, forward) {
11432 			var self = this, sel;
11433 
11434 			// Is IE specific range
11435 			if (rng.select) {
11436 				try {
11437 					rng.select();
11438 				} catch (ex) {
11439 					// Needed for some odd IE bug #1843306
11440 				}
11441 
11442 				return;
11443 			}
11444 
11445 			if (!self.tridentSel) {
11446 				sel = self.getSel();
11447 
11448 				if (sel) {
11449 					self.explicitRange = rng;
11450 
11451 					try {
11452 						sel.removeAllRanges();
11453 						sel.addRange(rng);
11454 					} catch (ex) {
11455 						// IE might throw errors here if the editor is within a hidden container and selection is changed
11456 					}
11457 
11458 					// Forward is set to false and we have an extend function
11459 					if (forward === false && sel.extend) {
11460 						sel.collapse(rng.endContainer, rng.endOffset);
11461 						sel.extend(rng.startContainer, rng.startOffset);
11462 					}
11463 
11464 					// adding range isn't always successful so we need to check range count otherwise an exception can occur
11465 					self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
11466 				}
11467 			} else {
11468 				// Is W3C Range fake range on IE
11469 				if (rng.cloneRange) {
11470 					try {
11471 						self.tridentSel.addRange(rng);
11472 						return;
11473 					} catch (ex) {
11474 						//IE9 throws an error here if called before selection is placed in the editor
11475 					}
11476 				}
11477 			}
11478 		},
11479 
11480 		/**
11481 		 * Sets the current selection to the specified DOM element.
11482 		 *
11483 		 * @method setNode
11484 		 * @param {Element} elm Element to set as the contents of the selection.
11485 		 * @return {Element} Returns the element that got passed in.
11486 		 * @example
11487 		 * // Inserts a DOM node at current selection/caret location
11488 		 * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'}));
11489 		 */
11490 		setNode: function(elm) {
11491 			var self = this;
11492 
11493 			self.setContent(self.dom.getOuterHTML(elm));
11494 
11495 			return elm;
11496 		},
11497 
11498 		/**
11499 		 * Returns the currently selected element or the common ancestor element for both start and end of the selection.
11500 		 *
11501 		 * @method getNode
11502 		 * @return {Element} Currently selected element or common ancestor element.
11503 		 * @example
11504 		 * // Alerts the currently selected elements node name
11505 		 * alert(tinymce.activeEditor.selection.getNode().nodeName);
11506 		 */
11507 		getNode: function() {
11508 			var self = this, rng = self.getRng(), elm;
11509 			var startContainer = rng.startContainer, endContainer = rng.endContainer;
11510 			var startOffset = rng.startOffset, endOffset = rng.endOffset, root = self.dom.getRoot();
11511 
11512 			function skipEmptyTextNodes(node, forwards) {
11513 				var orig = node;
11514 
11515 				while (node && node.nodeType === 3 && node.length === 0) {
11516 					node = forwards ? node.nextSibling : node.previousSibling;
11517 				}
11518 
11519 				return node || orig;
11520 			}
11521 
11522 			// Range maybe lost after the editor is made visible again
11523 			if (!rng) {
11524 				return root;
11525 			}
11526 
11527 			if (rng.setStart) {
11528 				elm = rng.commonAncestorContainer;
11529 
11530 				// Handle selection a image or other control like element such as anchors
11531 				if (!rng.collapsed) {
11532 					if (startContainer == endContainer) {
11533 						if (endOffset - startOffset < 2) {
11534 							if (startContainer.hasChildNodes()) {
11535 								elm = startContainer.childNodes[startOffset];
11536 							}
11537 						}
11538 					}
11539 
11540 					// If the anchor node is a element instead of a text node then return this element
11541 					//if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
11542 					//	return sel.anchorNode.childNodes[sel.anchorOffset];
11543 
11544 					// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
11545 					// This happens when you double click an underlined word in FireFox.
11546 					if (startContainer.nodeType === 3 && endContainer.nodeType === 3) {
11547 						if (startContainer.length === startOffset) {
11548 							startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);
11549 						} else {
11550 							startContainer = startContainer.parentNode;
11551 						}
11552 
11553 						if (endOffset === 0) {
11554 							endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);
11555 						} else {
11556 							endContainer = endContainer.parentNode;
11557 						}
11558 
11559 						if (startContainer && startContainer === endContainer) {
11560 							return startContainer;
11561 						}
11562 					}
11563 				}
11564 
11565 				if (elm && elm.nodeType == 3) {
11566 					return elm.parentNode;
11567 				}
11568 
11569 				return elm;
11570 			}
11571 
11572 			elm = rng.item ? rng.item(0) : rng.parentElement();
11573 
11574 			// IE 7 might return elements outside the iframe
11575 			if (elm.ownerDocument !== self.win.document) {
11576 				elm = root;
11577 			}
11578 
11579 			return elm;
11580 		},
11581 
11582 		getSelectedBlocks: function(startElm, endElm) {
11583 			var self = this, dom = self.dom, node, root, selectedBlocks = [];
11584 
11585 			root = dom.getRoot();
11586 			startElm = dom.getParent(startElm || self.getStart(), dom.isBlock);
11587 			endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock);
11588 
11589 			if (startElm && startElm != root) {
11590 				selectedBlocks.push(startElm);
11591 			}
11592 
11593 			if (startElm && endElm && startElm != endElm) {
11594 				node = startElm;
11595 
11596 				var walker = new TreeWalker(startElm, root);
11597 				while ((node = walker.next()) && node != endElm) {
11598 					if (dom.isBlock(node)) {
11599 						selectedBlocks.push(node);
11600 					}
11601 				}
11602 			}
11603 
11604 			if (endElm && startElm != endElm && endElm != root) {
11605 				selectedBlocks.push(endElm);
11606 			}
11607 
11608 			return selectedBlocks;
11609 		},
11610 
11611 		isForward: function() {
11612 			var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
11613 
11614 			// No support for selection direction then always return true
11615 			if (!sel || !sel.anchorNode || !sel.focusNode) {
11616 				return true;
11617 			}
11618 
11619 			anchorRange = dom.createRng();
11620 			anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
11621 			anchorRange.collapse(true);
11622 
11623 			focusRange = dom.createRng();
11624 			focusRange.setStart(sel.focusNode, sel.focusOffset);
11625 			focusRange.collapse(true);
11626 
11627 			return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
11628 		},
11629 
11630 		normalize: function() {
11631 			var self = this, rng = self.getRng();
11632 
11633 			if (!isIE && new RangeUtils(self.dom).normalize(rng)) {
11634 				self.setRng(rng, self.isForward());
11635 			}
11636 
11637 			return rng;
11638 		},
11639 
11640 		/**
11641 		 * Executes callback of the current selection matches the specified selector or not and passes the state and args to the callback.
11642 		 *
11643 		 * @method selectorChanged
11644 		 * @param {String} selector CSS selector to check for.
11645 		 * @param {function} callback Callback with state and args when the selector is matches or not.
11646 		 */
11647 		selectorChanged: function(selector, callback) {
11648 			var self = this, currentSelectors;
11649 
11650 			if (!self.selectorChangedData) {
11651 				self.selectorChangedData = {};
11652 				currentSelectors = {};
11653 
11654 				self.editor.on('NodeChange', function(e) {
11655 					var node = e.element, dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
11656 
11657 					// Check for new matching selectors
11658 					each(self.selectorChangedData, function(callbacks, selector) {
11659 						each(parents, function(node) {
11660 							if (dom.is(node, selector)) {
11661 								if (!currentSelectors[selector]) {
11662 									// Execute callbacks
11663 									each(callbacks, function(callback) {
11664 										callback(true, {node: node, selector: selector, parents: parents});
11665 									});
11666 
11667 									currentSelectors[selector] = callbacks;
11668 								}
11669 
11670 								matchedSelectors[selector] = callbacks;
11671 								return false;
11672 							}
11673 						});
11674 					});
11675 
11676 					// Check if current selectors still match
11677 					each(currentSelectors, function(callbacks, selector) {
11678 						if (!matchedSelectors[selector]) {
11679 							delete currentSelectors[selector];
11680 
11681 							each(callbacks, function(callback) {
11682 								callback(false, {node: node, selector: selector, parents: parents});
11683 							});
11684 						}
11685 					});
11686 				});
11687 			}
11688 
11689 			// Add selector listeners
11690 			if (!self.selectorChangedData[selector]) {
11691 				self.selectorChangedData[selector] = [];
11692 			}
11693 
11694 			self.selectorChangedData[selector].push(callback);
11695 
11696 			return self;
11697 		},
11698 
11699 		getScrollContainer: function() {
11700 			var scrollContainer, node = this.dom.getRoot();
11701 
11702 			while (node && node.nodeName != 'BODY') {
11703 				if (node.scrollHeight > node.clientHeight) {
11704 					scrollContainer = node;
11705 					break;
11706 				}
11707 
11708 				node = node.parentNode;
11709 			}
11710 
11711 			return scrollContainer;
11712 		},
11713 
11714 		scrollIntoView: function(elm) {
11715 			var y, viewPort, self = this, dom = self.dom, root = dom.getRoot(), viewPortY, viewPortH;
11716 
11717 			function getPos(elm) {
11718 				var x = 0, y = 0;
11719 
11720 				var offsetParent = elm;
11721 				while (offsetParent && offsetParent.nodeType) {
11722 					x += offsetParent.offsetLeft || 0;
11723 					y += offsetParent.offsetTop || 0;
11724 					offsetParent = offsetParent.offsetParent;
11725 				}
11726 
11727 				return {x: x, y: y};
11728 			}
11729 
11730 			if (root.nodeName != 'BODY') {
11731 				var scrollContainer = self.getScrollContainer();
11732 				if (scrollContainer) {
11733 					y = getPos(elm).y - getPos(scrollContainer).y;
11734 					viewPortH = scrollContainer.clientHeight;
11735 					viewPortY = scrollContainer.scrollTop;
11736 					if (y < viewPortY || y + 25 > viewPortY + viewPortH) {
11737 						scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25;
11738 					}
11739 
11740 					return;
11741 				}
11742 			}
11743 
11744 			viewPort = dom.getViewPort(self.editor.getWin());
11745 			y = dom.getPos(elm).y;
11746 			viewPortY = viewPort.y;
11747 			viewPortH = viewPort.h;
11748 			if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
11749 				self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
11750 			}
11751 		},
11752 
11753 		_moveEndPoint: function(rng, node, start) {
11754 			var root = node, walker = new TreeWalker(node, root);
11755 			var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements();
11756 
11757 			do {
11758 				// Text node
11759 				if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
11760 					if (start) {
11761 						rng.setStart(node, 0);
11762 					} else {
11763 						rng.setEnd(node, node.nodeValue.length);
11764 					}
11765 
11766 					return;
11767 				}
11768 
11769 				// BR/IMG/INPUT elements
11770 				if (nonEmptyElementsMap[node.nodeName]) {
11771 					if (start) {
11772 						rng.setStartBefore(node);
11773 					} else {
11774 						if (node.nodeName == 'BR') {
11775 							rng.setEndBefore(node);
11776 						} else {
11777 							rng.setEndAfter(node);
11778 						}
11779 					}
11780 
11781 					return;
11782 				}
11783 
11784 				// Found empty text block old IE can place the selection inside those
11785 				if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) {
11786 					if (start) {
11787 						rng.setStart(node, 0);
11788 					} else {
11789 						rng.setEnd(node, 0);
11790 					}
11791 
11792 					return;
11793 				}
11794 			} while ((node = (start ? walker.next() : walker.prev())));
11795 
11796 			// Failed to find any text node or other suitable location then move to the root of body
11797 			if (root.nodeName == 'BODY') {
11798 				if (start) {
11799 					rng.setStart(root, 0);
11800 				} else {
11801 					rng.setEnd(root, root.childNodes.length);
11802 				}
11803 			}
11804 		},
11805 
11806 		destroy: function() {
11807 			this.win = null;
11808 			this.controlSelection.destroy();
11809 		}
11810 	};
11811 
11812 	return Selection;
11813 });
11814 
11815 // Included from: js/tinymce/classes/fmt/Preview.js
11816 
11817 /**
11818  * Preview.js
11819  *
11820  * Copyright, Moxiecode Systems AB
11821  * Released under LGPL License.
11822  *
11823  * License: http://www.tinymce.com/license
11824  * Contributing: http://www.tinymce.com/contributing
11825  */
11826 
11827 /**
11828  * Internal class for generating previews styles for formats.
11829  *
11830  * Example:
11831  *  Preview.getCssText(editor, 'bold');
11832  *
11833  * @class tinymce.fmt.Preview
11834  * @private
11835  */
11836 define("tinymce/fmt/Preview", [
11837 	"tinymce/util/Tools"
11838 ], function(Tools) {
11839 	var each = Tools.each;
11840 
11841 	function getCssText(editor, format) {
11842 		var name, previewElm, dom = editor.dom;
11843 		var previewCss = '', parentFontSize, previewStyles;
11844 
11845 		previewStyles = editor.settings.preview_styles;
11846 
11847 		// No preview forced
11848 		if (previewStyles === false) {
11849 			return '';
11850 		}
11851 
11852 		// Default preview
11853 		if (!previewStyles) {
11854 			previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
11855 				'text-transform color background-color border border-radius outline text-shadow';
11856 		}
11857 
11858 		// Removes any variables since these can't be previewed
11859 		function removeVars(val) {
11860 			return val.replace(/%(\w+)/g, '');
11861 		}
11862 
11863 		// Create block/inline element to use for preview
11864 		if (typeof(format) == "string") {
11865 			format = editor.formatter.get(format);
11866 			if (!format) {
11867 				return;
11868 			}
11869 
11870 			format = format[0];
11871 		}
11872 
11873 		name = format.block || format.inline || 'span';
11874 		previewElm = dom.create(name);
11875 
11876 		// Add format styles to preview element
11877 		each(format.styles, function(value, name) {
11878 			value = removeVars(value);
11879 
11880 			if (value) {
11881 				dom.setStyle(previewElm, name, value);
11882 			}
11883 		});
11884 
11885 		// Add attributes to preview element
11886 		each(format.attributes, function(value, name) {
11887 			value = removeVars(value);
11888 
11889 			if (value) {
11890 				dom.setAttrib(previewElm, name, value);
11891 			}
11892 		});
11893 
11894 		// Add classes to preview element
11895 		each(format.classes, function(value) {
11896 			value = removeVars(value);
11897 
11898 			if (!dom.hasClass(previewElm, value)) {
11899 				dom.addClass(previewElm, value);
11900 			}
11901 		});
11902 
11903 		editor.fire('PreviewFormats');
11904 
11905 		// Add the previewElm outside the visual area
11906 		dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
11907 		editor.getBody().appendChild(previewElm);
11908 
11909 		// Get parent container font size so we can compute px values out of em/% for older IE:s
11910 		parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
11911 		parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
11912 
11913 		each(previewStyles.split(' '), function(name) {
11914 			var value = dom.getStyle(previewElm, name, true);
11915 
11916 			// If background is transparent then check if the body has a background color we can use
11917 			if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
11918 				value = dom.getStyle(editor.getBody(), name, true);
11919 
11920 				// Ignore white since it's the default color, not the nicest fix
11921 				// TODO: Fix this by detecting runtime style
11922 				if (dom.toHex(value).toLowerCase() == '#ffffff') {
11923 					return;
11924 				}
11925 			}
11926 
11927 			if (name == 'color') {
11928 				// Ignore black since it's the default color, not the nicest fix
11929 				// TODO: Fix this by detecting runtime style
11930 				if (dom.toHex(value).toLowerCase() == '#000000') {
11931 					return;
11932 				}
11933 			}
11934 
11935 			// Old IE won't calculate the font size so we need to do that manually
11936 			if (name == 'font-size') {
11937 				if (/em|%$/.test(value)) {
11938 					if (parentFontSize === 0) {
11939 						return;
11940 					}
11941 
11942 					// Convert font size from em/% to px
11943 					value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
11944 					value = (value * parentFontSize) + 'px';
11945 				}
11946 			}
11947 
11948 			if (name == "border" && value) {
11949 				previewCss += 'padding:0 2px;';
11950 			}
11951 
11952 			previewCss += name + ':' + value + ';';
11953 		});
11954 
11955 		editor.fire('AfterPreviewFormats');
11956 
11957 		//previewCss += 'line-height:normal';
11958 
11959 		dom.remove(previewElm);
11960 
11961 		return previewCss;
11962 	}
11963 
11964 	return {
11965 		getCssText: getCssText
11966 	};
11967 });
11968 
11969 // Included from: js/tinymce/classes/Formatter.js
11970 
11971 /**
11972  * Formatter.js
11973  *
11974  * Copyright, Moxiecode Systems AB
11975  * Released under LGPL License.
11976  *
11977  * License: http://www.tinymce.com/license
11978  * Contributing: http://www.tinymce.com/contributing
11979  */
11980 
11981 /**
11982  * Text formatter engine class. This class is used to apply formats like bold, italic, font size
11983  * etc to the current selection or specific nodes. This engine was build to replace the browsers
11984  * default formatting logic for execCommand due to it's inconsistent and buggy behavior.
11985  *
11986  * @class tinymce.Formatter
11987  * @example
11988  *  tinymce.activeEditor.formatter.register('mycustomformat', {
11989  *    inline: 'span',
11990  *    styles: {color: '#ff0000'}
11991  *  });
11992  *
11993  *  tinymce.activeEditor.formatter.apply('mycustomformat');
11994  */
11995 define("tinymce/Formatter", [
11996 	"tinymce/dom/TreeWalker",
11997 	"tinymce/dom/RangeUtils",
11998 	"tinymce/util/Tools",
11999 	"tinymce/fmt/Preview"
12000 ], function(TreeWalker, RangeUtils, Tools, Preview) {
12001 	/**
12002 	 * Constructs a new formatter instance.
12003 	 *
12004 	 * @constructor Formatter
12005 	 * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to.
12006 	 */
12007 	return function(ed) {
12008 		var formats = {},
12009 			dom = ed.dom,
12010 			selection = ed.selection,
12011 			rangeUtils = new RangeUtils(dom),
12012 			isValid = ed.schema.isValidChild,
12013 			isBlock = dom.isBlock,
12014 			forcedRootBlock = ed.settings.forced_root_block,
12015 			nodeIndex = dom.nodeIndex,
12016 			INVISIBLE_CHAR = '\uFEFF',
12017 			MCE_ATTR_RE = /^(src|href|style)$/,
12018 			FALSE = false,
12019 			TRUE = true,
12020 			formatChangeData,
12021 			undef,
12022 			getContentEditable = dom.getContentEditable,
12023 			disableCaretContainer,
12024 			markCaretContainersBogus;
12025 
12026 		var each = Tools.each,
12027 			grep = Tools.grep,
12028 			walk = Tools.walk,
12029 			extend = Tools.extend;
12030 
12031 		function isTextBlock(name) {
12032 			if (name.nodeType) {
12033 				name = name.nodeName;
12034 			}
12035 
12036 			return !!ed.schema.getTextBlockElements()[name.toLowerCase()];
12037 		}
12038 
12039 		function getParents(node, selector) {
12040 			return dom.getParents(node, selector, dom.getRoot());
12041 		}
12042 
12043 		function isCaretNode(node) {
12044 			return node.nodeType === 1 && node.id === '_mce_caret';
12045 		}
12046 
12047 		function defaultFormats() {
12048 			register({
12049 				
12050 				valigntop: [
12051 					{selector: 'td,th', styles: {'verticalAlign': 'top'}}
12052 				],
12053 
12054 				valignmiddle: [
12055 					{selector: 'td,th', styles: {'verticalAlign': 'middle'}}
12056 				],
12057 
12058 				valignbottom: [
12059 					{selector: 'td,th', styles: {'verticalAlign': 'bottom'}}
12060 				],
12061 
12062 				alignleft: [
12063 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'left'}, defaultBlock: 'div'},
12064 					{selector: 'img,table', collapsed: false, styles: {'float': 'left'}}
12065 				],
12066 
12067 				aligncenter: [
12068 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'center'}, defaultBlock: 'div'},
12069 					{selector: 'img', collapsed: false, styles: {display: 'block', marginLeft: 'auto', marginRight: 'auto'}},
12070 					{selector: 'table', collapsed: false, styles: {marginLeft: 'auto', marginRight: 'auto'}}
12071 				],
12072 
12073 				alignright: [
12074 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'right'}, defaultBlock: 'div'},
12075 					{selector: 'img,table', collapsed: false, styles: {'float': 'right'}}
12076 				],
12077 
12078 				alignjustify: [
12079 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'justify'}, defaultBlock: 'div'}
12080 				],
12081 
12082 				bold: [
12083 					{inline: 'strong', remove: 'all'},
12084 					{inline: 'span', styles: {fontWeight: 'bold'}},
12085 					{inline: 'b', remove: 'all'}
12086 				],
12087 
12088 				italic: [
12089 					{inline: 'em', remove: 'all'},
12090 					{inline: 'span', styles: {fontStyle: 'italic'}},
12091 					{inline: 'i', remove: 'all'}
12092 				],
12093 
12094 				underline: [
12095 					{inline: 'span', styles: {textDecoration: 'underline'}, exact: true},
12096 					{inline: 'u', remove: 'all'}
12097 				],
12098 
12099 				strikethrough: [
12100 					{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true},
12101 					{inline: 'strike', remove: 'all'}
12102 				],
12103 
12104 				forecolor: {inline: 'span', styles: {color: '%value'}, wrap_links: false},
12105 				hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, wrap_links: false},
12106 				fontname: {inline: 'span', styles: {fontFamily: '%value'}},
12107 				fontsize: {inline: 'span', styles: {fontSize: '%value'}},
12108 				fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
12109 				blockquote: {block: 'blockquote', wrapper: 1, remove: 'all'},
12110 				subscript: {inline: 'sub'},
12111 				superscript: {inline: 'sup'},
12112 				code: {inline: 'code'},
12113 
12114 				link: {inline: 'a', selector: 'a', remove: 'all', split: true, deep: true,
12115 					onmatch: function() {
12116 						return true;
12117 					},
12118 
12119 					onformat: function(elm, fmt, vars) {
12120 						each(vars, function(value, key) {
12121 							dom.setAttrib(elm, key, value);
12122 						});
12123 					}
12124 				},
12125 
12126 				removeformat: [
12127 					{
12128 						selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q',
12129 						remove: 'all',
12130 						split: true,
12131 						expand: false,
12132 						block_expand: true,
12133 						deep: true
12134 					},
12135 					{selector: 'span', attributes: ['style', 'class'], remove: 'empty', split: true, expand: false, deep: true},
12136 					{selector: '*', attributes: ['style', 'class'], split: false, expand: false, deep: true}
12137 				]
12138 			});
12139 
12140 			// Register default block formats
12141 			each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) {
12142 				register(name, {block: name, remove: 'all'});
12143 			});
12144 
12145 			// Register user defined formats
12146 			register(ed.settings.formats);
12147 		}
12148 
12149 		function addKeyboardShortcuts() {
12150 			// Add some inline shortcuts
12151 			ed.addShortcut('ctrl+b', 'bold_desc', 'Bold');
12152 			ed.addShortcut('ctrl+i', 'italic_desc', 'Italic');
12153 			ed.addShortcut('ctrl+u', 'underline_desc', 'Underline');
12154 
12155 			// BlockFormat shortcuts keys
12156 			for (var i = 1; i <= 6; i++) {
12157 				ed.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
12158 			}
12159 
12160 			ed.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
12161 			ed.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
12162 			ed.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
12163 		}
12164 
12165 		// Public functions
12166 
12167 		/**
12168 		 * Returns the format by name or all formats if no name is specified.
12169 		 *
12170 		 * @method get
12171 		 * @param {String} name Optional name to retrive by.
12172 		 * @return {Array/Object} Array/Object with all registred formats or a specific format.
12173 		 */
12174 		function get(name) {
12175 			return name ? formats[name] : formats;
12176 		}
12177 
12178 		/**
12179 		 * Registers a specific format by name.
12180 		 *
12181 		 * @method register
12182 		 * @param {Object/String} name Name of the format for example "bold".
12183 		 * @param {Object/Array} format Optional format object or array of format variants
12184 		 * can only be omitted if the first arg is an object.
12185 		 */
12186 		function register(name, format) {
12187 			if (name) {
12188 				if (typeof(name) !== 'string') {
12189 					each(name, function(format, name) {
12190 						register(name, format);
12191 					});
12192 				} else {
12193 					// Force format into array and add it to internal collection
12194 					format = format.length ? format : [format];
12195 
12196 					each(format, function(format) {
12197 						// Set deep to false by default on selector formats this to avoid removing
12198 						// alignment on images inside paragraphs when alignment is changed on paragraphs
12199 						if (format.deep === undef) {
12200 							format.deep = !format.selector;
12201 						}
12202 
12203 						// Default to true
12204 						if (format.split === undef) {
12205 							format.split = !format.selector || format.inline;
12206 						}
12207 
12208 						// Default to true
12209 						if (format.remove === undef && format.selector && !format.inline) {
12210 							format.remove = 'none';
12211 						}
12212 
12213 						// Mark format as a mixed format inline + block level
12214 						if (format.selector && format.inline) {
12215 							format.mixed = true;
12216 							format.block_expand = true;
12217 						}
12218 
12219 						// Split classes if needed
12220 						if (typeof(format.classes) === 'string') {
12221 							format.classes = format.classes.split(/\s+/);
12222 						}
12223 					});
12224 
12225 					formats[name] = format;
12226 				}
12227 			}
12228 		}
12229 
12230 		function getTextDecoration(node) {
12231 			var decoration;
12232 
12233 			ed.dom.getParent(node, function(n) {
12234 				decoration = ed.dom.getStyle(n, 'text-decoration');
12235 				return decoration && decoration !== 'none';
12236 			});
12237 
12238 			return decoration;
12239 		}
12240 
12241 		function processUnderlineAndColor(node) {
12242 			var textDecoration;
12243 			if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {
12244 				textDecoration = getTextDecoration(node.parentNode);
12245 				if (ed.dom.getStyle(node, 'color') && textDecoration) {
12246 					ed.dom.setStyle(node, 'text-decoration', textDecoration);
12247 				} else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {
12248 					ed.dom.setStyle(node, 'text-decoration', null);
12249 				}
12250 			}
12251 		}
12252 
12253 		/**
12254 		 * Applies the specified format to the current selection or specified node.
12255 		 *
12256 		 * @method apply
12257 		 * @param {String} name Name of format to apply.
12258 		 * @param {Object} vars Optional list of variables to replace within format before applying it.
12259 		 * @param {Node} node Optional node to apply the format to defaults to current selection.
12260 		 */
12261 		function apply(name, vars, node) {
12262 			var formatList = get(name), format = formatList[0], bookmark, rng, isCollapsed = !node && selection.isCollapsed();
12263 
12264 			function setElementFormat(elm, fmt) {
12265 				fmt = fmt || format;
12266 
12267 				if (elm) {
12268 					if (fmt.onformat) {
12269 						fmt.onformat(elm, fmt, vars, node);
12270 					}
12271 
12272 					each(fmt.styles, function(value, name) {
12273 						dom.setStyle(elm, name, replaceVars(value, vars));
12274 					});
12275 
12276 					// Needed for the WebKit span spam bug
12277 					// TODO: Remove this once WebKit/Blink fixes this
12278 					if (fmt.styles) {
12279 						var styleVal = dom.getAttrib(elm, 'style');
12280 
12281 						if (styleVal) {
12282 							elm.setAttribute('data-mce-style', styleVal);
12283 						}
12284 					}
12285 
12286 					each(fmt.attributes, function(value, name) {
12287 						dom.setAttrib(elm, name, replaceVars(value, vars));
12288 					});
12289 
12290 					each(fmt.classes, function(value) {
12291 						value = replaceVars(value, vars);
12292 
12293 						if (!dom.hasClass(elm, value)) {
12294 							dom.addClass(elm, value);
12295 						}
12296 					});
12297 				}
12298 			}
12299 
12300 			function adjustSelectionToVisibleSelection() {
12301 				function findSelectionEnd(start, end) {
12302 					var walker = new TreeWalker(end);
12303 					for (node = walker.current(); node; node = walker.prev()) {
12304 						if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
12305 							return node;
12306 						}
12307 					}
12308 				}
12309 
12310 				// Adjust selection so that a end container with a end offset of zero is not included in the selection
12311 				// as this isn't visible to the user.
12312 				var rng = ed.selection.getRng();
12313 				var start = rng.startContainer;
12314 				var end = rng.endContainer;
12315 
12316 				if (start != end && rng.endOffset === 0) {
12317 					var newEnd = findSelectionEnd(start, end);
12318 					var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
12319 
12320 					rng.setEnd(newEnd, endOffset);
12321 				}
12322 
12323 				return rng;
12324 			}
12325 
12326 			function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){
12327 				var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;
12328 
12329 				// find the index of the first child list.
12330 				each(node.childNodes, function(n, index) {
12331 					if (n.nodeName === "UL" || n.nodeName === "OL") {
12332 						listIndex = index;
12333 						list = n;
12334 						return false;
12335 					}
12336 				});
12337 
12338 				// get the index of the bookmarks
12339 				each(node.childNodes, function(n, index) {
12340 					if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
12341 						if (n.id == bookmark.id + "_start") {
12342 							startIndex = index;
12343 						} else if (n.id == bookmark.id + "_end") {
12344 							endIndex = index;
12345 						}
12346 					}
12347 				});
12348 
12349 				// if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally
12350 				if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {
12351 					each(grep(node.childNodes), process);
12352 					return 0;
12353 				} else {
12354 					currentWrapElm = dom.clone(wrapElm, FALSE);
12355 
12356 					// create a list of the nodes on the same side of the list as the selection
12357 					each(grep(node.childNodes), function(n, index) {
12358 						if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {
12359 							nodes.push(n);
12360 							n.parentNode.removeChild(n);
12361 						}
12362 					});
12363 
12364 					// insert the wrapping element either before or after the list.
12365 					if (startIndex < listIndex) {
12366 						node.insertBefore(currentWrapElm, list);
12367 					} else if (startIndex > listIndex) {
12368 						node.insertBefore(currentWrapElm, list.nextSibling);
12369 					}
12370 
12371 					// add the new nodes to the list.
12372 					newWrappers.push(currentWrapElm);
12373 
12374 					each(nodes, function(node) {
12375 						currentWrapElm.appendChild(node);
12376 					});
12377 
12378 					return currentWrapElm;
12379 				}
12380 			}
12381 
12382 			function applyRngStyle(rng, bookmark, node_specific) {
12383 				var newWrappers = [], wrapName, wrapElm, contentEditable = true;
12384 
12385 				// Setup wrapper element
12386 				wrapName = format.inline || format.block;
12387 				wrapElm = dom.create(wrapName);
12388 				setElementFormat(wrapElm);
12389 
12390 				rangeUtils.walk(rng, function(nodes) {
12391 					var currentWrapElm;
12392 
12393 					/**
12394 					 * Process a list of nodes wrap them.
12395 					 */
12396 					function process(node) {
12397 						var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
12398 
12399 						lastContentEditable = contentEditable;
12400 						nodeName = node.nodeName.toLowerCase();
12401 						parentName = node.parentNode.nodeName.toLowerCase();
12402 
12403 						// Node has a contentEditable value
12404 						if (node.nodeType === 1 && getContentEditable(node)) {
12405 							lastContentEditable = contentEditable;
12406 							contentEditable = getContentEditable(node) === "true";
12407 							hasContentEditableState = true; // We don't want to wrap the container only it's children
12408 						}
12409 
12410 						// Stop wrapping on br elements
12411 						if (isEq(nodeName, 'br')) {
12412 							currentWrapElm = 0;
12413 
12414 							// Remove any br elements when we wrap things
12415 							if (format.block) {
12416 								dom.remove(node);
12417 							}
12418 
12419 							return;
12420 						}
12421 
12422 						// If node is wrapper type
12423 						if (format.wrapper && matchNode(node, name, vars)) {
12424 							currentWrapElm = 0;
12425 							return;
12426 						}
12427 
12428 						// Can we rename the block
12429 						// TODO: Break this if up, too complex
12430 						if (contentEditable && !hasContentEditableState && format.block &&
12431 							!format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) {
12432 							node = dom.rename(node, wrapName);
12433 							setElementFormat(node);
12434 							newWrappers.push(node);
12435 							currentWrapElm = 0;
12436 							return;
12437 						}
12438 
12439 						// Handle selector patterns
12440 						if (format.selector) {
12441 							// Look for matching formats
12442 							each(formatList, function(format) {
12443 								// Check collapsed state if it exists
12444 								if ('collapsed' in format && format.collapsed !== isCollapsed) {
12445 									return;
12446 								}
12447 
12448 								if (dom.is(node, format.selector) && !isCaretNode(node)) {
12449 									setElementFormat(node, format);
12450 									found = true;
12451 								}
12452 							});
12453 
12454 							// Continue processing if a selector match wasn't found and a inline element is defined
12455 							if (!format.inline || found) {
12456 								currentWrapElm = 0;
12457 								return;
12458 							}
12459 						}
12460 
12461 						// Is it valid to wrap this item
12462 						// TODO: Break this if up, too complex
12463 						if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
12464 								!(!node_specific && node.nodeType === 3 &&
12465 								node.nodeValue.length === 1 &&
12466 								node.nodeValue.charCodeAt(0) === 65279) &&
12467 								!isCaretNode(node) &&
12468 								(!format.inline || !isBlock(node))) {
12469 							// Start wrapping
12470 							if (!currentWrapElm) {
12471 								// Wrap the node
12472 								currentWrapElm = dom.clone(wrapElm, FALSE);
12473 								node.parentNode.insertBefore(currentWrapElm, node);
12474 								newWrappers.push(currentWrapElm);
12475 							}
12476 
12477 							currentWrapElm.appendChild(node);
12478 						} else if (nodeName == 'li' && bookmark) {
12479 							// Start wrapping - if we are in a list node and have a bookmark, then
12480 							// we will always begin by wrapping in a new element.
12481 							currentWrapElm = applyStyleToList(node, bookmark, wrapElm, newWrappers, process);
12482 						} else {
12483 							// Start a new wrapper for possible children
12484 							currentWrapElm = 0;
12485 
12486 							each(grep(node.childNodes), process);
12487 
12488 							if (hasContentEditableState) {
12489 								contentEditable = lastContentEditable; // Restore last contentEditable state from stack
12490 							}
12491 
12492 							// End the last wrapper
12493 							currentWrapElm = 0;
12494 						}
12495 					}
12496 
12497 					// Process siblings from range
12498 					each(nodes, process);
12499 				});
12500 
12501 				// Wrap links inside as well, for example color inside a link when the wrapper is around the link
12502 				if (format.wrap_links === false) {
12503 					each(newWrappers, function(node) {
12504 						function process(node) {
12505 							var i, currentWrapElm, children;
12506 
12507 							if (node.nodeName === 'A') {
12508 								currentWrapElm = dom.clone(wrapElm, FALSE);
12509 								newWrappers.push(currentWrapElm);
12510 
12511 								children = grep(node.childNodes);
12512 								for (i = 0; i < children.length; i++) {
12513 									currentWrapElm.appendChild(children[i]);
12514 								}
12515 
12516 								node.appendChild(currentWrapElm);
12517 							}
12518 
12519 							each(grep(node.childNodes), process);
12520 						}
12521 
12522 						process(node);
12523 					});
12524 				}
12525 
12526 				// Cleanup
12527 				each(newWrappers, function(node) {
12528 					var childCount;
12529 
12530 					function getChildCount(node) {
12531 						var count = 0;
12532 
12533 						each(node.childNodes, function(node) {
12534 							if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) {
12535 								count++;
12536 							}
12537 						});
12538 
12539 						return count;
12540 					}
12541 
12542 					function mergeStyles(node) {
12543 						var child, clone;
12544 
12545 						each(node.childNodes, function(node) {
12546 							if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {
12547 								child = node;
12548 								return FALSE; // break loop
12549 							}
12550 						});
12551 
12552 						// If child was found and of the same type as the current node
12553 						if (child && !isBookmarkNode(child) && matchName(child, format)) {
12554 							clone = dom.clone(child, FALSE);
12555 							setElementFormat(clone);
12556 
12557 							dom.replace(clone, node, TRUE);
12558 							dom.remove(child, 1);
12559 						}
12560 
12561 						return clone || node;
12562 					}
12563 
12564 					childCount = getChildCount(node);
12565 
12566 					// Remove empty nodes but only if there is multiple wrappers and they are not block
12567 					// elements so never remove single <h1></h1> since that would remove the
12568 					// currrent empty block element where the caret is at
12569 					if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {
12570 						dom.remove(node, 1);
12571 						return;
12572 					}
12573 
12574 					if (format.inline || format.wrapper) {
12575 						// Merges the current node with it's children of similar type to reduce the number of elements
12576 						if (!format.exact && childCount === 1) {
12577 							node = mergeStyles(node);
12578 						}
12579 
12580 						// Remove/merge children
12581 						each(formatList, function(format) {
12582 							// Merge all children of similar type will move styles from child to parent
12583 							// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
12584 							// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
12585 							each(dom.select(format.inline, node), function(child) {
12586 								var parent;
12587 
12588 								if (isBookmarkNode(child)) {
12589 									return;
12590 								}
12591 
12592 								// When wrap_links is set to false we don't want
12593 								// to remove the format on children within links
12594 								if (format.wrap_links === false) {
12595 									parent = child.parentNode;
12596 
12597 									do {
12598 										if (parent.nodeName === 'A') {
12599 											return;
12600 										}
12601 									} while ((parent = parent.parentNode));
12602 								}
12603 
12604 								removeFormat(format, vars, child, format.exact ? child : null);
12605 							});
12606 						});
12607 
12608 						// Remove child if direct parent is of same type
12609 						if (matchNode(node.parentNode, name, vars)) {
12610 							dom.remove(node, 1);
12611 							node = 0;
12612 							return TRUE;
12613 						}
12614 
12615 						// Look for parent with similar style format
12616 						if (format.merge_with_parents) {
12617 							dom.getParent(node.parentNode, function(parent) {
12618 								if (matchNode(parent, name, vars)) {
12619 									dom.remove(node, 1);
12620 									node = 0;
12621 									return TRUE;
12622 								}
12623 							});
12624 						}
12625 
12626 						// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
12627 						if (node && format.merge_siblings !== false) {
12628 							node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
12629 							node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
12630 						}
12631 					}
12632 				});
12633 			}
12634 
12635 			if (format) {
12636 				if (node) {
12637 					if (node.nodeType) {
12638 						rng = dom.createRng();
12639 						rng.setStartBefore(node);
12640 						rng.setEndAfter(node);
12641 						applyRngStyle(expandRng(rng, formatList), null, true);
12642 					} else {
12643 						applyRngStyle(node, null, true);
12644 					}
12645 				} else {
12646 					if (!isCollapsed || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
12647 						// Obtain selection node before selection is unselected by applyRngStyle()
12648 						var curSelNode = ed.selection.getNode();
12649 
12650 						// If the formats have a default block and we can't find a parent block then
12651 						// start wrapping it with a DIV this is for forced_root_blocks: false
12652 						// It's kind of a hack but people should be using the default block type P since all desktop editors work that way
12653 						if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
12654 							apply(formatList[0].defaultBlock);
12655 						}
12656 
12657 						// Apply formatting to selection
12658 						ed.selection.setRng(adjustSelectionToVisibleSelection());
12659 						bookmark = selection.getBookmark();
12660 						applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);
12661 
12662 						// Colored nodes should be underlined so that the color of the underline matches the text color.
12663 						if (format.styles && (format.styles.color || format.styles.textDecoration)) {
12664 							walk(curSelNode, processUnderlineAndColor, 'childNodes');
12665 							processUnderlineAndColor(curSelNode);
12666 						}
12667 
12668 						selection.moveToBookmark(bookmark);
12669 						moveStart(selection.getRng(TRUE));
12670 						ed.nodeChanged();
12671 					} else {
12672 						performCaretAction('apply', name, vars);
12673 					}
12674 				}
12675 			}
12676 		}
12677 
12678 		/**
12679 		 * Removes the specified format from the current selection or specified node.
12680 		 *
12681 		 * @method remove
12682 		 * @param {String} name Name of format to remove.
12683 		 * @param {Object} vars Optional list of variables to replace within format before removing it.
12684 		 * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
12685 		 */
12686 		function remove(name, vars, node) {
12687 			var formatList = get(name), format = formatList[0], bookmark, rng, contentEditable = true;
12688 
12689 			// Merges the styles for each node
12690 			function process(node) {
12691 				var children, i, l, lastContentEditable, hasContentEditableState;
12692 
12693 				// Node has a contentEditable value
12694 				if (node.nodeType === 1 && getContentEditable(node)) {
12695 					lastContentEditable = contentEditable;
12696 					contentEditable = getContentEditable(node) === "true";
12697 					hasContentEditableState = true; // We don't want to wrap the container only it's children
12698 				}
12699 
12700 				// Grab the children first since the nodelist might be changed
12701 				children = grep(node.childNodes);
12702 
12703 				// Process current node
12704 				if (contentEditable && !hasContentEditableState) {
12705 					for (i = 0, l = formatList.length; i < l; i++) {
12706 						if (removeFormat(formatList[i], vars, node, node)) {
12707 							break;
12708 						}
12709 					}
12710 				}
12711 
12712 				// Process the children
12713 				if (format.deep) {
12714 					if (children.length) {
12715 						for (i = 0, l = children.length; i < l; i++) {
12716 							process(children[i]);
12717 						}
12718 
12719 						if (hasContentEditableState) {
12720 							contentEditable = lastContentEditable; // Restore last contentEditable state from stack
12721 						}
12722 					}
12723 				}
12724 			}
12725 
12726 			function findFormatRoot(container) {
12727 				var formatRoot;
12728 
12729 				// Find format root
12730 				each(getParents(container.parentNode).reverse(), function(parent) {
12731 					var format;
12732 
12733 					// Find format root element
12734 					if (!formatRoot && parent.id != '_start' && parent.id != '_end') {
12735 						// Is the node matching the format we are looking for
12736 						format = matchNode(parent, name, vars);
12737 						if (format && format.split !== false) {
12738 							formatRoot = parent;
12739 						}
12740 					}
12741 				});
12742 
12743 				return formatRoot;
12744 			}
12745 
12746 			function wrapAndSplit(format_root, container, target, split) {
12747 				var parent, clone, lastClone, firstClone, i, formatRootParent;
12748 
12749 				// Format root found then clone formats and split it
12750 				if (format_root) {
12751 					formatRootParent = format_root.parentNode;
12752 
12753 					for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
12754 						clone = dom.clone(parent, FALSE);
12755 
12756 						for (i = 0; i < formatList.length; i++) {
12757 							if (removeFormat(formatList[i], vars, clone, clone)) {
12758 								clone = 0;
12759 								break;
12760 							}
12761 						}
12762 
12763 						// Build wrapper node
12764 						if (clone) {
12765 							if (lastClone) {
12766 								clone.appendChild(lastClone);
12767 							}
12768 
12769 							if (!firstClone) {
12770 								firstClone = clone;
12771 							}
12772 
12773 							lastClone = clone;
12774 						}
12775 					}
12776 
12777 					// Never split block elements if the format is mixed
12778 					if (split && (!format.mixed || !isBlock(format_root))) {
12779 						container = dom.split(format_root, container);
12780 					}
12781 
12782 					// Wrap container in cloned formats
12783 					if (lastClone) {
12784 						target.parentNode.insertBefore(lastClone, target);
12785 						firstClone.appendChild(target);
12786 					}
12787 				}
12788 
12789 				return container;
12790 			}
12791 
12792 			function splitToFormatRoot(container) {
12793 				return wrapAndSplit(findFormatRoot(container), container, container, true);
12794 			}
12795 
12796 			function unwrap(start) {
12797 				var node = dom.get(start ? '_start' : '_end'),
12798 					out = node[start ? 'firstChild' : 'lastChild'];
12799 
12800 				// If the end is placed within the start the result will be removed
12801 				// So this checks if the out node is a bookmark node if it is it
12802 				// checks for another more suitable node
12803 				if (isBookmarkNode(out)) {
12804 					out = out[start ? 'firstChild' : 'lastChild'];
12805 				}
12806 
12807 				dom.remove(node, true);
12808 
12809 				return out;
12810 			}
12811 
12812 			function removeRngStyle(rng) {
12813 				var startContainer, endContainer;
12814 				var commonAncestorContainer = rng.commonAncestorContainer;
12815 
12816 				rng = expandRng(rng, formatList, TRUE);
12817 
12818 				if (format.split) {
12819 					startContainer = getContainer(rng, TRUE);
12820 					endContainer = getContainer(rng);
12821 
12822 					if (startContainer != endContainer) {
12823 						// WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN
12824 						// so let's see if we can use the first child instead
12825 						// This will happen if you triple click a table cell and use remove formatting
12826 						if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
12827 							if (startContainer.nodeName == "TR") {
12828 								startContainer = startContainer.firstChild.firstChild || startContainer;
12829 							} else {
12830 								startContainer = startContainer.firstChild || startContainer;
12831 							}
12832 						}
12833 
12834 						// Try to adjust endContainer as well if cells on the same row were selected - bug #6410
12835 						if (commonAncestorContainer &&
12836 							/^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) &&
12837 							/^(TH|TD)$/.test(endContainer.nodeName) && endContainer.firstChild) {
12838 							endContainer = endContainer.firstChild || endContainer;
12839 						}
12840 
12841 						// Wrap start/end nodes in span element since these might be cloned/moved
12842 						startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
12843 						endContainer = wrap(endContainer, 'span', {id: '_end', 'data-mce-type': 'bookmark'});
12844 
12845 						// Split start/end
12846 						splitToFormatRoot(startContainer);
12847 						splitToFormatRoot(endContainer);
12848 
12849 						// Unwrap start/end to get real elements again
12850 						startContainer = unwrap(TRUE);
12851 						endContainer = unwrap();
12852 					} else {
12853 						startContainer = endContainer = splitToFormatRoot(startContainer);
12854 					}
12855 
12856 					// Update range positions since they might have changed after the split operations
12857 					rng.startContainer = startContainer.parentNode;
12858 					rng.startOffset = nodeIndex(startContainer);
12859 					rng.endContainer = endContainer.parentNode;
12860 					rng.endOffset = nodeIndex(endContainer) + 1;
12861 				}
12862 
12863 				// Remove items between start/end
12864 				rangeUtils.walk(rng, function(nodes) {
12865 					each(nodes, function(node) {
12866 						process(node);
12867 
12868 						// Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.
12869 						if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' &&
12870 							node.parentNode && getTextDecoration(node.parentNode) === 'underline') {
12871 							removeFormat({
12872 								'deep': false,
12873 								'exact': true,
12874 								'inline': 'span',
12875 								'styles': {
12876 									'textDecoration': 'underline'
12877 								}
12878 							}, null, node);
12879 						}
12880 					});
12881 				});
12882 			}
12883 
12884 			// Handle node
12885 			if (node) {
12886 				if (node.nodeType) {
12887 					rng = dom.createRng();
12888 					rng.setStartBefore(node);
12889 					rng.setEndAfter(node);
12890 					removeRngStyle(rng);
12891 				} else {
12892 					removeRngStyle(node);
12893 				}
12894 
12895 				return;
12896 			}
12897 
12898 			if (!selection.isCollapsed() || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
12899 				bookmark = selection.getBookmark();
12900 				removeRngStyle(selection.getRng(TRUE));
12901 				selection.moveToBookmark(bookmark);
12902 
12903 				// Check if start element still has formatting then we are at: "<b>text|</b>text"
12904 				// and need to move the start into the next text node
12905 				if (format.inline && match(name, vars, selection.getStart())) {
12906 					moveStart(selection.getRng(true));
12907 				}
12908 
12909 				ed.nodeChanged();
12910 			} else {
12911 				performCaretAction('remove', name, vars);
12912 			}
12913 		}
12914 
12915 		/**
12916 		 * Toggles the specified format on/off.
12917 		 *
12918 		 * @method toggle
12919 		 * @param {String} name Name of format to apply/remove.
12920 		 * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
12921 		 * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
12922 		 */
12923 		function toggle(name, vars, node) {
12924 			var fmt = get(name);
12925 
12926 			if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {
12927 				remove(name, vars, node);
12928 			} else {
12929 				apply(name, vars, node);
12930 			}
12931 		}
12932 
12933 		/**
12934 		 * Return true/false if the specified node has the specified format.
12935 		 *
12936 		 * @method matchNode
12937 		 * @param {Node} node Node to check the format on.
12938 		 * @param {String} name Format name to check.
12939 		 * @param {Object} vars Optional list of variables to replace before checking it.
12940 		 * @param {Boolean} similar Match format that has similar properties.
12941 		 * @return {Object} Returns the format object it matches or undefined if it doesn't match.
12942 		 */
12943 		function matchNode(node, name, vars, similar) {
12944 			var formatList = get(name), format, i, classes;
12945 
12946 			function matchItems(node, format, item_name) {
12947 				var key, value, items = format[item_name], i;
12948 
12949 				// Custom match
12950 				if (format.onmatch) {
12951 					return format.onmatch(node, format, item_name);
12952 				}
12953 
12954 				// Check all items
12955 				if (items) {
12956 					// Non indexed object
12957 					if (items.length === undef) {
12958 						for (key in items) {
12959 							if (items.hasOwnProperty(key)) {
12960 								if (item_name === 'attributes') {
12961 									value = dom.getAttrib(node, key);
12962 								} else {
12963 									value = getStyle(node, key);
12964 								}
12965 
12966 								if (similar && !value && !format.exact) {
12967 									return;
12968 								}
12969 
12970 								if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) {
12971 									return;
12972 								}
12973 							}
12974 						}
12975 					} else {
12976 						// Only one match needed for indexed arrays
12977 						for (i = 0; i < items.length; i++) {
12978 							if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) {
12979 								return format;
12980 							}
12981 						}
12982 					}
12983 				}
12984 
12985 				return format;
12986 			}
12987 
12988 			if (formatList && node) {
12989 				// Check each format in list
12990 				for (i = 0; i < formatList.length; i++) {
12991 					format = formatList[i];
12992 
12993 					// Name name, attributes, styles and classes
12994 					if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {
12995 						// Match classes
12996 						if ((classes = format.classes)) {
12997 							for (i = 0; i < classes.length; i++) {
12998 								if (!dom.hasClass(node, classes[i])) {
12999 									return;
13000 								}
13001 							}
13002 						}
13003 
13004 						return format;
13005 					}
13006 				}
13007 			}
13008 		}
13009 
13010 		/**
13011 		 * Matches the current selection or specified node against the specified format name.
13012 		 *
13013 		 * @method match
13014 		 * @param {String} name Name of format to match.
13015 		 * @param {Object} vars Optional list of variables to replace before checking it.
13016 		 * @param {Node} node Optional node to check.
13017 		 * @return {boolean} true/false if the specified selection/node matches the format.
13018 		 */
13019 		function match(name, vars, node) {
13020 			var startNode;
13021 
13022 			function matchParents(node) {
13023 				var root = dom.getRoot();
13024 
13025 				if (node === root) {
13026 					return false;
13027 				}
13028 
13029 				// Find first node with similar format settings
13030 				node = dom.getParent(node, function(node) {
13031 					return node.parentNode === root || !!matchNode(node, name, vars, true);
13032 				});
13033 
13034 				// Do an exact check on the similar format element
13035 				return matchNode(node, name, vars);
13036 			}
13037 
13038 			// Check specified node
13039 			if (node) {
13040 				return matchParents(node);
13041 			}
13042 
13043 			// Check selected node
13044 			node = selection.getNode();
13045 			if (matchParents(node)) {
13046 				return TRUE;
13047 			}
13048 
13049 			// Check start node if it's different
13050 			startNode = selection.getStart();
13051 			if (startNode != node) {
13052 				if (matchParents(startNode)) {
13053 					return TRUE;
13054 				}
13055 			}
13056 
13057 			return FALSE;
13058 		}
13059 
13060 		/**
13061 		 * Matches the current selection against the array of formats and returns a new array with matching formats.
13062 		 *
13063 		 * @method matchAll
13064 		 * @param {Array} names Name of format to match.
13065 		 * @param {Object} vars Optional list of variables to replace before checking it.
13066 		 * @return {Array} Array with matched formats.
13067 		 */
13068 		function matchAll(names, vars) {
13069 			var startElement, matchedFormatNames = [], checkedMap = {};
13070 
13071 			// Check start of selection for formats
13072 			startElement = selection.getStart();
13073 			dom.getParent(startElement, function(node) {
13074 				var i, name;
13075 
13076 				for (i = 0; i < names.length; i++) {
13077 					name = names[i];
13078 
13079 					if (!checkedMap[name] && matchNode(node, name, vars)) {
13080 						checkedMap[name] = true;
13081 						matchedFormatNames.push(name);
13082 					}
13083 				}
13084 			}, dom.getRoot());
13085 
13086 			return matchedFormatNames;
13087 		}
13088 
13089 		/**
13090 		 * Returns true/false if the specified format can be applied to the current selection or not. It
13091 		 * will currently only check the state for selector formats, it returns true on all other format types.
13092 		 *
13093 		 * @method canApply
13094 		 * @param {String} name Name of format to check.
13095 		 * @return {boolean} true/false if the specified format can be applied to the current selection/node.
13096 		 */
13097 		function canApply(name) {
13098 			var formatList = get(name), startNode, parents, i, x, selector;
13099 
13100 			if (formatList) {
13101 				startNode = selection.getStart();
13102 				parents = getParents(startNode);
13103 
13104 				for (x = formatList.length - 1; x >= 0; x--) {
13105 					selector = formatList[x].selector;
13106 
13107 					// Format is not selector based then always return TRUE
13108 					// Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line
13109 					if (!selector || formatList[x].defaultBlock) {
13110 						return TRUE;
13111 					}
13112 
13113 					for (i = parents.length - 1; i >= 0; i--) {
13114 						if (dom.is(parents[i], selector)) {
13115 							return TRUE;
13116 						}
13117 					}
13118 				}
13119 			}
13120 
13121 			return FALSE;
13122 		}
13123 
13124 		/**
13125 		 * Executes the specified callback when the current selection matches the formats or not.
13126 		 *
13127 		 * @method formatChanged
13128 		 * @param {String} formats Comma separated list of formats to check for.
13129 		 * @param {function} callback Callback with state and args when the format is changed/toggled on/off.
13130 		 * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
13131 		 */
13132 		function formatChanged(formats, callback, similar) {
13133 			var currentFormats;
13134 
13135 			// Setup format node change logic
13136 			if (!formatChangeData) {
13137 				formatChangeData = {};
13138 				currentFormats = {};
13139 
13140 				ed.on('NodeChange', function(e) {
13141 					var parents = getParents(e.element), matchedFormats = {};
13142 
13143 					// Check for new formats
13144 					each(formatChangeData, function(callbacks, format) {
13145 						each(parents, function(node) {
13146 							if (matchNode(node, format, {}, callbacks.similar)) {
13147 								if (!currentFormats[format]) {
13148 									// Execute callbacks
13149 									each(callbacks, function(callback) {
13150 										callback(true, {node: node, format: format, parents: parents});
13151 									});
13152 
13153 									currentFormats[format] = callbacks;
13154 								}
13155 
13156 								matchedFormats[format] = callbacks;
13157 								return false;
13158 							}
13159 						});
13160 					});
13161 
13162 					// Check if current formats still match
13163 					each(currentFormats, function(callbacks, format) {
13164 						if (!matchedFormats[format]) {
13165 							delete currentFormats[format];
13166 
13167 							each(callbacks, function(callback) {
13168 								callback(false, {node: e.element, format: format, parents: parents});
13169 							});
13170 						}
13171 					});
13172 				});
13173 			}
13174 
13175 			// Add format listeners
13176 			each(formats.split(','), function(format) {
13177 				if (!formatChangeData[format]) {
13178 					formatChangeData[format] = [];
13179 					formatChangeData[format].similar = similar;
13180 				}
13181 
13182 				formatChangeData[format].push(callback);
13183 			});
13184 
13185 			return this;
13186 		}
13187 
13188 		/**
13189 		 * Returns a preview css text for the specified format.
13190 		 *
13191 		 * @method getCssText
13192 		 * @param {String/Object} format Format to generate preview css text for.
13193 		 * @return {String} Css text for the specified format.
13194 		 * @example
13195 		 * var cssText1 = editor.formatter.getCssText('bold');
13196 		 * var cssText2 = editor.formatter.getCssText({inline: 'b'});
13197 		 */
13198 		function getCssText(format) {
13199 			return Preview.getCssText(ed, format);
13200 		}
13201 
13202 		// Expose to public
13203 		extend(this, {
13204 			get: get,
13205 			register: register,
13206 			apply: apply,
13207 			remove: remove,
13208 			toggle: toggle,
13209 			match: match,
13210 			matchAll: matchAll,
13211 			matchNode: matchNode,
13212 			canApply: canApply,
13213 			formatChanged: formatChanged,
13214 			getCssText: getCssText
13215 		});
13216 
13217 		// Initialize
13218 		defaultFormats();
13219 		addKeyboardShortcuts();
13220 		ed.on('BeforeGetContent', function() {
13221 			if (markCaretContainersBogus) {
13222 				markCaretContainersBogus();
13223 			}
13224 		});
13225 		ed.on('mouseup keydown', function(e) {
13226 			if (disableCaretContainer) {
13227 				disableCaretContainer(e);
13228 			}
13229 		});
13230 
13231 		// Private functions
13232 
13233 		/**
13234 		 * Checks if the specified nodes name matches the format inline/block or selector.
13235 		 *
13236 		 * @private
13237 		 * @param {Node} node Node to match against the specified format.
13238 		 * @param {Object} format Format object o match with.
13239 		 * @return {boolean} true/false if the format matches.
13240 		 */
13241 		function matchName(node, format) {
13242 			// Check for inline match
13243 			if (isEq(node, format.inline)) {
13244 				return TRUE;
13245 			}
13246 
13247 			// Check for block match
13248 			if (isEq(node, format.block)) {
13249 				return TRUE;
13250 			}
13251 
13252 			// Check for selector match
13253 			if (format.selector) {
13254 				return node.nodeType == 1 && dom.is(node, format.selector);
13255 			}
13256 		}
13257 
13258 		/**
13259 		 * Compares two string/nodes regardless of their case.
13260 		 *
13261 		 * @private
13262 		 * @param {String/Node} Node or string to compare.
13263 		 * @param {String/Node} Node or string to compare.
13264 		 * @return {boolean} True/false if they match.
13265 		 */
13266 		function isEq(str1, str2) {
13267 			str1 = str1 || '';
13268 			str2 = str2 || '';
13269 
13270 			str1 = '' + (str1.nodeName || str1);
13271 			str2 = '' + (str2.nodeName || str2);
13272 
13273 			return str1.toLowerCase() == str2.toLowerCase();
13274 		}
13275 
13276 		/**
13277 		 * Returns the style by name on the specified node. This method modifies the style
13278 		 * contents to make it more easy to match. This will resolve a few browser issues.
13279 		 *
13280 		 * @private
13281 		 * @param {Node} node to get style from.
13282 		 * @param {String} name Style name to get.
13283 		 * @return {String} Style item value.
13284 		 */
13285 		function getStyle(node, name) {
13286 			return normalizeStyleValue(dom.getStyle(node, name), name);
13287 		}
13288 
13289 		/**
13290 		 * Normalize style value by name. This method modifies the style contents
13291 		 * to make it more easy to match. This will resolve a few browser issues.
13292 		 *
13293 		 * @private
13294 		 * @param {Node} node to get style from.
13295 		 * @param {String} name Style name to get.
13296 		 * @return {String} Style item value.
13297 		 */
13298 		function normalizeStyleValue(value, name) {
13299 			// Force the format to hex
13300 			if (name == 'color' || name == 'backgroundColor') {
13301 				value = dom.toHex(value);
13302 			}
13303 
13304 			// Opera will return bold as 700
13305 			if (name == 'fontWeight' && value == 700) {
13306 				value = 'bold';
13307 			}
13308 
13309 			// Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font"
13310 			if (name == 'fontFamily') {
13311 				value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ',');
13312 			}
13313 
13314 			return '' + value;
13315 		}
13316 
13317 		/**
13318 		 * Replaces variables in the value. The variable format is %var.
13319 		 *
13320 		 * @private
13321 		 * @param {String} value Value to replace variables in.
13322 		 * @param {Object} vars Name/value array with variables to replace.
13323 		 * @return {String} New value with replaced variables.
13324 		 */
13325 		function replaceVars(value, vars) {
13326 			if (typeof(value) != "string") {
13327 				value = value(vars);
13328 			} else if (vars) {
13329 				value = value.replace(/%(\w+)/g, function(str, name) {
13330 					return vars[name] || str;
13331 				});
13332 			}
13333 
13334 			return value;
13335 		}
13336 
13337 		function isWhiteSpaceNode(node) {
13338 			return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
13339 		}
13340 
13341 		function wrap(node, name, attrs) {
13342 			var wrapper = dom.create(name, attrs);
13343 
13344 			node.parentNode.insertBefore(wrapper, node);
13345 			wrapper.appendChild(node);
13346 
13347 			return wrapper;
13348 		}
13349 
13350 		/**
13351 		 * Expands the specified range like object to depending on format.
13352 		 *
13353 		 * For example on block formats it will move the start/end position
13354 		 * to the beginning of the current block.
13355 		 *
13356 		 * @private
13357 		 * @param {Object} rng Range like object.
13358 		 * @param {Array} formats Array with formats to expand by.
13359 		 * @return {Object} Expanded range like object.
13360 		 */
13361 		function expandRng(rng, format, remove) {
13362 			var lastIdx, leaf, endPoint,
13363 				startContainer = rng.startContainer,
13364 				startOffset = rng.startOffset,
13365 				endContainer = rng.endContainer,
13366 				endOffset = rng.endOffset;
13367 
13368 			// This function walks up the tree if there is no siblings before/after the node
13369 			function findParentContainer(start) {
13370 				var container, parent, sibling, siblingName, root;
13371 
13372 				container = parent = start ? startContainer : endContainer;
13373 				siblingName = start ? 'previousSibling' : 'nextSibling';
13374 				root = dom.getRoot();
13375 
13376 				function isBogusBr(node) {
13377 					return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
13378 				}
13379 
13380 				// If it's a text node and the offset is inside the text
13381 				if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
13382 					if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
13383 						return container;
13384 					}
13385 				}
13386 
13387 				/*eslint no-constant-condition:0 */
13388 				while (true) {
13389 					// Stop expanding on block elements
13390 					if (!format[0].block_expand && isBlock(parent)) {
13391 						return parent;
13392 					}
13393 
13394 					// Walk left/right
13395 					for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
13396 						if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
13397 							return parent;
13398 						}
13399 					}
13400 
13401 					// Check if we can move up are we at root level or body level
13402 					if (parent.parentNode == root) {
13403 						container = parent;
13404 						break;
13405 					}
13406 
13407 					parent = parent.parentNode;
13408 				}
13409 
13410 				return container;
13411 			}
13412 
13413 			// This function walks down the tree to find the leaf at the selection.
13414 			// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
13415 			function findLeaf(node, offset) {
13416 				if (offset === undef) {
13417 					offset = node.nodeType === 3 ? node.length : node.childNodes.length;
13418 				}
13419 
13420 				while (node && node.hasChildNodes()) {
13421 					node = node.childNodes[offset];
13422 					if (node) {
13423 						offset = node.nodeType === 3 ? node.length : node.childNodes.length;
13424 					}
13425 				}
13426 				return { node: node, offset: offset };
13427 			}
13428 
13429 			// If index based start position then resolve it
13430 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
13431 				lastIdx = startContainer.childNodes.length - 1;
13432 				startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];
13433 
13434 				if (startContainer.nodeType == 3) {
13435 					startOffset = 0;
13436 				}
13437 			}
13438 
13439 			// If index based end position then resolve it
13440 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
13441 				lastIdx = endContainer.childNodes.length - 1;
13442 				endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];
13443 
13444 				if (endContainer.nodeType == 3) {
13445 					endOffset = endContainer.nodeValue.length;
13446 				}
13447 			}
13448 
13449 			// Expands the node to the closes contentEditable false element if it exists
13450 			function findParentContentEditable(node) {
13451 				var parent = node;
13452 
13453 				while (parent) {
13454 					if (parent.nodeType === 1 && getContentEditable(parent)) {
13455 						return getContentEditable(parent) === "false" ? parent : node;
13456 					}
13457 
13458 					parent = parent.parentNode;
13459 				}
13460 
13461 				return node;
13462 			}
13463 
13464 			function findWordEndPoint(container, offset, start) {
13465 				var walker, node, pos, lastTextNode;
13466 
13467 				function findSpace(node, offset) {
13468 					var pos, pos2, str = node.nodeValue;
13469 
13470 					if (typeof(offset) == "undefined") {
13471 						offset = start ? str.length : 0;
13472 					}
13473 
13474 					if (start) {
13475 						pos = str.lastIndexOf(' ', offset);
13476 						pos2 = str.lastIndexOf('\u00a0', offset);
13477 						pos = pos > pos2 ? pos : pos2;
13478 
13479 						// Include the space on remove to avoid tag soup
13480 						if (pos !== -1 && !remove) {
13481 							pos++;
13482 						}
13483 					} else {
13484 						pos = str.indexOf(' ', offset);
13485 						pos2 = str.indexOf('\u00a0', offset);
13486 						pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
13487 					}
13488 
13489 					return pos;
13490 				}
13491 
13492 				if (container.nodeType === 3) {
13493 					pos = findSpace(container, offset);
13494 
13495 					if (pos !== -1) {
13496 						return {container: container, offset: pos};
13497 					}
13498 
13499 					lastTextNode = container;
13500 				}
13501 
13502 				// Walk the nodes inside the block
13503 				walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
13504 				while ((node = walker[start ? 'prev' : 'next']())) {
13505 					if (node.nodeType === 3) {
13506 						lastTextNode = node;
13507 						pos = findSpace(node);
13508 
13509 						if (pos !== -1) {
13510 							return {container: node, offset: pos};
13511 						}
13512 					} else if (isBlock(node)) {
13513 						break;
13514 					}
13515 				}
13516 
13517 				if (lastTextNode) {
13518 					if (start) {
13519 						offset = 0;
13520 					} else {
13521 						offset = lastTextNode.length;
13522 					}
13523 
13524 					return {container: lastTextNode, offset: offset};
13525 				}
13526 			}
13527 
13528 			function findSelectorEndPoint(container, sibling_name) {
13529 				var parents, i, y, curFormat;
13530 
13531 				if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name]) {
13532 					container = container[sibling_name];
13533 				}
13534 
13535 				parents = getParents(container);
13536 				for (i = 0; i < parents.length; i++) {
13537 					for (y = 0; y < format.length; y++) {
13538 						curFormat = format[y];
13539 
13540 						// If collapsed state is set then skip formats that doesn't match that
13541 						if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) {
13542 							continue;
13543 						}
13544 
13545 						if (dom.is(parents[i], curFormat.selector)) {
13546 							return parents[i];
13547 						}
13548 					}
13549 				}
13550 
13551 				return container;
13552 			}
13553 
13554 			function findBlockEndPoint(container, sibling_name) {
13555 				var node, root = dom.getRoot();
13556 
13557 				// Expand to block of similar type
13558 				if (!format[0].wrapper) {
13559 					node = dom.getParent(container, format[0].block, root);
13560 				}
13561 
13562 				// Expand to first wrappable block element or any block element
13563 				if (!node) {
13564 					node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) {
13565 						// Fixes #6183 where it would expand to editable parent element in inline mode
13566 						return node != root && isTextBlock(node);
13567 					});
13568 				}
13569 
13570 				// Exclude inner lists from wrapping
13571 				if (node && format[0].wrapper) {
13572 					node = getParents(node, 'ul,ol').reverse()[0] || node;
13573 				}
13574 
13575 				// Didn't find a block element look for first/last wrappable element
13576 				if (!node) {
13577 					node = container;
13578 
13579 					while (node[sibling_name] && !isBlock(node[sibling_name])) {
13580 						node = node[sibling_name];
13581 
13582 						// Break on BR but include it will be removed later on
13583 						// we can't remove it now since we need to check if it can be wrapped
13584 						if (isEq(node, 'br')) {
13585 							break;
13586 						}
13587 					}
13588 				}
13589 
13590 				return node || container;
13591 			}
13592 
13593 			// Expand to closest contentEditable element
13594 			startContainer = findParentContentEditable(startContainer);
13595 			endContainer = findParentContentEditable(endContainer);
13596 
13597 			// Exclude bookmark nodes if possible
13598 			if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
13599 				startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
13600 				startContainer = startContainer.nextSibling || startContainer;
13601 
13602 				if (startContainer.nodeType == 3) {
13603 					startOffset = 0;
13604 				}
13605 			}
13606 
13607 			if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
13608 				endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
13609 				endContainer = endContainer.previousSibling || endContainer;
13610 
13611 				if (endContainer.nodeType == 3) {
13612 					endOffset = endContainer.length;
13613 				}
13614 			}
13615 
13616 			if (format[0].inline) {
13617 				if (rng.collapsed) {
13618 					// Expand left to closest word boundary
13619 					endPoint = findWordEndPoint(startContainer, startOffset, true);
13620 					if (endPoint) {
13621 						startContainer = endPoint.container;
13622 						startOffset = endPoint.offset;
13623 					}
13624 
13625 					// Expand right to closest word boundary
13626 					endPoint = findWordEndPoint(endContainer, endOffset);
13627 					if (endPoint) {
13628 						endContainer = endPoint.container;
13629 						endOffset = endPoint.offset;
13630 					}
13631 				}
13632 
13633 				// Avoid applying formatting to a trailing space.
13634 				leaf = findLeaf(endContainer, endOffset);
13635 				if (leaf.node) {
13636 					while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) {
13637 						leaf = findLeaf(leaf.node.previousSibling);
13638 					}
13639 
13640 					if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
13641 							leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
13642 
13643 						if (leaf.offset > 1) {
13644 							endContainer = leaf.node;
13645 							endContainer.splitText(leaf.offset - 1);
13646 						}
13647 					}
13648 				}
13649 			}
13650 
13651 			// Move start/end point up the tree if the leaves are sharp and if we are in different containers
13652 			// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
13653 			// This will reduce the number of wrapper elements that needs to be created
13654 			// Move start point up the tree
13655 			if (format[0].inline || format[0].block_expand) {
13656 				if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
13657 					startContainer = findParentContainer(true);
13658 				}
13659 
13660 				if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
13661 					endContainer = findParentContainer();
13662 				}
13663 			}
13664 
13665 			// Expand start/end container to matching selector
13666 			if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
13667 				// Find new startContainer/endContainer if there is better one
13668 				startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
13669 				endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
13670 			}
13671 
13672 			// Expand start/end container to matching block element or text node
13673 			if (format[0].block || format[0].selector) {
13674 				// Find new startContainer/endContainer if there is better one
13675 				startContainer = findBlockEndPoint(startContainer, 'previousSibling');
13676 				endContainer = findBlockEndPoint(endContainer, 'nextSibling');
13677 
13678 				// Non block element then try to expand up the leaf
13679 				if (format[0].block) {
13680 					if (!isBlock(startContainer)) {
13681 						startContainer = findParentContainer(true);
13682 					}
13683 
13684 					if (!isBlock(endContainer)) {
13685 						endContainer = findParentContainer();
13686 					}
13687 				}
13688 			}
13689 
13690 			// Setup index for startContainer
13691 			if (startContainer.nodeType == 1) {
13692 				startOffset = nodeIndex(startContainer);
13693 				startContainer = startContainer.parentNode;
13694 			}
13695 
13696 			// Setup index for endContainer
13697 			if (endContainer.nodeType == 1) {
13698 				endOffset = nodeIndex(endContainer) + 1;
13699 				endContainer = endContainer.parentNode;
13700 			}
13701 
13702 			// Return new range like object
13703 			return {
13704 				startContainer: startContainer,
13705 				startOffset: startOffset,
13706 				endContainer: endContainer,
13707 				endOffset: endOffset
13708 			};
13709 		}
13710 
13711 		/**
13712 		 * Removes the specified format for the specified node. It will also remove the node if it doesn't have
13713 		 * any attributes if the format specifies it to do so.
13714 		 *
13715 		 * @private
13716 		 * @param {Object} format Format object with items to remove from node.
13717 		 * @param {Object} vars Name/value object with variables to apply to format.
13718 		 * @param {Node} node Node to remove the format styles on.
13719 		 * @param {Node} compare_node Optional compare node, if specified the styles will be compared to that node.
13720 		 * @return {Boolean} True/false if the node was removed or not.
13721 		 */
13722 		function removeFormat(format, vars, node, compare_node) {
13723 			var i, attrs, stylesModified;
13724 
13725 			// Check if node matches format
13726 			if (!matchName(node, format)) {
13727 				return FALSE;
13728 			}
13729 
13730 			// Should we compare with format attribs and styles
13731 			if (format.remove != 'all') {
13732 				// Remove styles
13733 				each(format.styles, function(value, name) {
13734 					value = normalizeStyleValue(replaceVars(value, vars), name);
13735 
13736 					// Indexed array
13737 					if (typeof(name) === 'number') {
13738 						name = value;
13739 						compare_node = 0;
13740 					}
13741 
13742 					if (!compare_node || isEq(getStyle(compare_node, name), value)) {
13743 						dom.setStyle(node, name, '');
13744 					}
13745 
13746 					stylesModified = 1;
13747 				});
13748 
13749 				// Remove style attribute if it's empty
13750 				if (stylesModified && dom.getAttrib(node, 'style') === '') {
13751 					node.removeAttribute('style');
13752 					node.removeAttribute('data-mce-style');
13753 				}
13754 
13755 				// Remove attributes
13756 				each(format.attributes, function(value, name) {
13757 					var valueOut;
13758 
13759 					value = replaceVars(value, vars);
13760 
13761 					// Indexed array
13762 					if (typeof(name) === 'number') {
13763 						name = value;
13764 						compare_node = 0;
13765 					}
13766 
13767 					if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {
13768 						// Keep internal classes
13769 						if (name == 'class') {
13770 							value = dom.getAttrib(node, name);
13771 							if (value) {
13772 								// Build new class value where everything is removed except the internal prefixed classes
13773 								valueOut = '';
13774 								each(value.split(/\s+/), function(cls) {
13775 									if (/mce\w+/.test(cls)) {
13776 										valueOut += (valueOut ? ' ' : '') + cls;
13777 									}
13778 								});
13779 
13780 								// We got some internal classes left
13781 								if (valueOut) {
13782 									dom.setAttrib(node, name, valueOut);
13783 									return;
13784 								}
13785 							}
13786 						}
13787 
13788 						// IE6 has a bug where the attribute doesn't get removed correctly
13789 						if (name == "class") {
13790 							node.removeAttribute('className');
13791 						}
13792 
13793 						// Remove mce prefixed attributes
13794 						if (MCE_ATTR_RE.test(name)) {
13795 							node.removeAttribute('data-mce-' + name);
13796 						}
13797 
13798 						node.removeAttribute(name);
13799 					}
13800 				});
13801 
13802 				// Remove classes
13803 				each(format.classes, function(value) {
13804 					value = replaceVars(value, vars);
13805 
13806 					if (!compare_node || dom.hasClass(compare_node, value)) {
13807 						dom.removeClass(node, value);
13808 					}
13809 				});
13810 
13811 				// Check for non internal attributes
13812 				attrs = dom.getAttribs(node);
13813 				for (i = 0; i < attrs.length; i++) {
13814 					if (attrs[i].nodeName.indexOf('_') !== 0) {
13815 						return FALSE;
13816 					}
13817 				}
13818 			}
13819 
13820 			// Remove the inline child if it's empty for example <b> or <span>
13821 			if (format.remove != 'none') {
13822 				removeNode(node, format);
13823 				return TRUE;
13824 			}
13825 		}
13826 
13827 		/**
13828 		 * Removes the node and wrap it's children in paragraphs before doing so or
13829 		 * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled.
13830 		 *
13831 		 * If the div in the node below gets removed:
13832 		 *  text<div>text</div>text
13833 		 *
13834 		 * Output becomes:
13835 		 *  text<div><br />text<br /></div>text
13836 		 *
13837 		 * So when the div is removed the result is:
13838 		 *  text<br />text<br />text
13839 		 *
13840 		 * @private
13841 		 * @param {Node} node Node to remove + apply BR/P elements to.
13842 		 * @param {Object} format Format rule.
13843 		 * @return {Node} Input node.
13844 		 */
13845 		function removeNode(node, format) {
13846 			var parentNode = node.parentNode, rootBlockElm;
13847 
13848 			function find(node, next, inc) {
13849 				node = getNonWhiteSpaceSibling(node, next, inc);
13850 
13851 				return !node || (node.nodeName == 'BR' || isBlock(node));
13852 			}
13853 
13854 			if (format.block) {
13855 				if (!forcedRootBlock) {
13856 					// Append BR elements if needed before we remove the block
13857 					if (isBlock(node) && !isBlock(parentNode)) {
13858 						if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) {
13859 							node.insertBefore(dom.create('br'), node.firstChild);
13860 						}
13861 
13862 						if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) {
13863 							node.appendChild(dom.create('br'));
13864 						}
13865 					}
13866 				} else {
13867 					// Wrap the block in a forcedRootBlock if we are at the root of document
13868 					if (parentNode == dom.getRoot()) {
13869 						if (!format.list_block || !isEq(node, format.list_block)) {
13870 							each(grep(node.childNodes), function(node) {
13871 								if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {
13872 									if (!rootBlockElm) {
13873 										rootBlockElm = wrap(node, forcedRootBlock);
13874 										dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs);
13875 									} else {
13876 										rootBlockElm.appendChild(node);
13877 									}
13878 								} else {
13879 									rootBlockElm = 0;
13880 								}
13881 							});
13882 						}
13883 					}
13884 				}
13885 			}
13886 
13887 			// Never remove nodes that isn't the specified inline element if a selector is specified too
13888 			if (format.selector && format.inline && !isEq(format.inline, node)) {
13889 				return;
13890 			}
13891 
13892 			dom.remove(node, 1);
13893 		}
13894 
13895 		/**
13896 		 * Returns the next/previous non whitespace node.
13897 		 *
13898 		 * @private
13899 		 * @param {Node} node Node to start at.
13900 		 * @param {boolean} next (Optional) Include next or previous node defaults to previous.
13901 		 * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false.
13902 		 * @return {Node} Next or previous node or undefined if it wasn't found.
13903 		 */
13904 		function getNonWhiteSpaceSibling(node, next, inc) {
13905 			if (node) {
13906 				next = next ? 'nextSibling' : 'previousSibling';
13907 
13908 				for (node = inc ? node : node[next]; node; node = node[next]) {
13909 					if (node.nodeType == 1 || !isWhiteSpaceNode(node)) {
13910 						return node;
13911 					}
13912 				}
13913 			}
13914 		}
13915 
13916 		/**
13917 		 * Checks if the specified node is a bookmark node or not.
13918 		 *
13919 		 * @private
13920 		 * @param {Node} node Node to check if it's a bookmark node or not.
13921 		 * @return {Boolean} true/false if the node is a bookmark node.
13922 		 */
13923 		function isBookmarkNode(node) {
13924 			return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';
13925 		}
13926 
13927 		/**
13928 		 * Merges the next/previous sibling element if they match.
13929 		 *
13930 		 * @private
13931 		 * @param {Node} prev Previous node to compare/merge.
13932 		 * @param {Node} next Next node to compare/merge.
13933 		 * @return {Node} Next node if we didn't merge and prev node if we did.
13934 		 */
13935 		function mergeSiblings(prev, next) {
13936 			var sibling, tmpSibling;
13937 
13938 			/**
13939 			 * Compares two nodes and checks if it's attributes and styles matches.
13940 			 * This doesn't compare classes as items since their order is significant.
13941 			 *
13942 			 * @private
13943 			 * @param {Node} node1 First node to compare with.
13944 			 * @param {Node} node2 Second node to compare with.
13945 			 * @return {boolean} True/false if the nodes are the same or not.
13946 			 */
13947 			function compareElements(node1, node2) {
13948 				// Not the same name
13949 				if (node1.nodeName != node2.nodeName) {
13950 					return FALSE;
13951 				}
13952 
13953 				/**
13954 				 * Returns all the nodes attributes excluding internal ones, styles and classes.
13955 				 *
13956 				 * @private
13957 				 * @param {Node} node Node to get attributes from.
13958 				 * @return {Object} Name/value object with attributes and attribute values.
13959 				 */
13960 				function getAttribs(node) {
13961 					var attribs = {};
13962 
13963 					each(dom.getAttribs(node), function(attr) {
13964 						var name = attr.nodeName.toLowerCase();
13965 
13966 						// Don't compare internal attributes or style
13967 						if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
13968 							attribs[name] = dom.getAttrib(node, name);
13969 						}
13970 					});
13971 
13972 					return attribs;
13973 				}
13974 
13975 				/**
13976 				 * Compares two objects checks if it's key + value exists in the other one.
13977 				 *
13978 				 * @private
13979 				 * @param {Object} obj1 First object to compare.
13980 				 * @param {Object} obj2 Second object to compare.
13981 				 * @return {boolean} True/false if the objects matches or not.
13982 				 */
13983 				function compareObjects(obj1, obj2) {
13984 					var value, name;
13985 
13986 					for (name in obj1) {
13987 						// Obj1 has item obj2 doesn't have
13988 						if (obj1.hasOwnProperty(name)) {
13989 							value = obj2[name];
13990 
13991 							// Obj2 doesn't have obj1 item
13992 							if (value === undef) {
13993 								return FALSE;
13994 							}
13995 
13996 							// Obj2 item has a different value
13997 							if (obj1[name] != value) {
13998 								return FALSE;
13999 							}
14000 
14001 							// Delete similar value
14002 							delete obj2[name];
14003 						}
14004 					}
14005 
14006 					// Check if obj 2 has something obj 1 doesn't have
14007 					for (name in obj2) {
14008 						// Obj2 has item obj1 doesn't have
14009 						if (obj2.hasOwnProperty(name)) {
14010 							return FALSE;
14011 						}
14012 					}
14013 
14014 					return TRUE;
14015 				}
14016 
14017 				// Attribs are not the same
14018 				if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
14019 					return FALSE;
14020 				}
14021 
14022 				// Styles are not the same
14023 				if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
14024 					return FALSE;
14025 				}
14026 
14027 				return !isBookmarkNode(node1) && !isBookmarkNode(node2);
14028 			}
14029 
14030 			function findElementSibling(node, sibling_name) {
14031 				for (sibling = node; sibling; sibling = sibling[sibling_name]) {
14032 					if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) {
14033 						return node;
14034 					}
14035 
14036 					if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) {
14037 						return sibling;
14038 					}
14039 				}
14040 
14041 				return node;
14042 			}
14043 
14044 			// Check if next/prev exists and that they are elements
14045 			if (prev && next) {
14046 				// If previous sibling is empty then jump over it
14047 				prev = findElementSibling(prev, 'previousSibling');
14048 				next = findElementSibling(next, 'nextSibling');
14049 
14050 				// Compare next and previous nodes
14051 				if (compareElements(prev, next)) {
14052 					// Append nodes between
14053 					for (sibling = prev.nextSibling; sibling && sibling != next;) {
14054 						tmpSibling = sibling;
14055 						sibling = sibling.nextSibling;
14056 						prev.appendChild(tmpSibling);
14057 					}
14058 
14059 					// Remove next node
14060 					dom.remove(next);
14061 
14062 					// Move children into prev node
14063 					each(grep(next.childNodes), function(node) {
14064 						prev.appendChild(node);
14065 					});
14066 
14067 					return prev;
14068 				}
14069 			}
14070 
14071 			return next;
14072 		}
14073 
14074 		function getContainer(rng, start) {
14075 			var container, offset, lastIdx;
14076 
14077 			container = rng[start ? 'startContainer' : 'endContainer'];
14078 			offset = rng[start ? 'startOffset' : 'endOffset'];
14079 
14080 			if (container.nodeType == 1) {
14081 				lastIdx = container.childNodes.length - 1;
14082 
14083 				if (!start && offset) {
14084 					offset--;
14085 				}
14086 
14087 				container = container.childNodes[offset > lastIdx ? lastIdx : offset];
14088 			}
14089 
14090 			// If start text node is excluded then walk to the next node
14091 			if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
14092 				container = new TreeWalker(container, ed.getBody()).next() || container;
14093 			}
14094 
14095 			// If end text node is excluded then walk to the previous node
14096 			if (container.nodeType === 3 && !start && offset === 0) {
14097 				container = new TreeWalker(container, ed.getBody()).prev() || container;
14098 			}
14099 
14100 			return container;
14101 		}
14102 
14103 		function performCaretAction(type, name, vars) {
14104 			var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
14105 
14106 			// Creates a caret container bogus element
14107 			function createCaretContainer(fill) {
14108 				var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
14109 
14110 				if (fill) {
14111 					caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
14112 				}
14113 
14114 				return caretContainer;
14115 			}
14116 
14117 			function isCaretContainerEmpty(node, nodes) {
14118 				while (node) {
14119 					if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
14120 						return false;
14121 					}
14122 
14123 					// Collect nodes
14124 					if (nodes && node.nodeType === 1) {
14125 						nodes.push(node);
14126 					}
14127 
14128 					node = node.firstChild;
14129 				}
14130 
14131 				return true;
14132 			}
14133 
14134 			// Returns any parent caret container element
14135 			function getParentCaretContainer(node) {
14136 				while (node) {
14137 					if (node.id === caretContainerId) {
14138 						return node;
14139 					}
14140 
14141 					node = node.parentNode;
14142 				}
14143 			}
14144 
14145 			// Finds the first text node in the specified node
14146 			function findFirstTextNode(node) {
14147 				var walker;
14148 
14149 				if (node) {
14150 					walker = new TreeWalker(node, node);
14151 
14152 					for (node = walker.current(); node; node = walker.next()) {
14153 						if (node.nodeType === 3) {
14154 							return node;
14155 						}
14156 					}
14157 				}
14158 			}
14159 
14160 			// Removes the caret container for the specified node or all on the current document
14161 			function removeCaretContainer(node, move_caret) {
14162 				var child, rng;
14163 
14164 				if (!node) {
14165 					node = getParentCaretContainer(selection.getStart());
14166 
14167 					if (!node) {
14168 						while ((node = dom.get(caretContainerId))) {
14169 							removeCaretContainer(node, false);
14170 						}
14171 					}
14172 				} else {
14173 					rng = selection.getRng(true);
14174 
14175 					if (isCaretContainerEmpty(node)) {
14176 						if (move_caret !== false) {
14177 							rng.setStartBefore(node);
14178 							rng.setEndBefore(node);
14179 						}
14180 
14181 						dom.remove(node);
14182 					} else {
14183 						child = findFirstTextNode(node);
14184 
14185 						if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
14186 							child = child.deleteData(0, 1);
14187 						}
14188 
14189 						dom.remove(node, 1);
14190 					}
14191 
14192 					selection.setRng(rng);
14193 				}
14194 			}
14195 
14196 			// Applies formatting to the caret postion
14197 			function applyCaretFormat() {
14198 				var rng, caretContainer, textNode, offset, bookmark, container, text;
14199 
14200 				rng = selection.getRng(true);
14201 				offset = rng.startOffset;
14202 				container = rng.startContainer;
14203 				text = container.nodeValue;
14204 
14205 				caretContainer = getParentCaretContainer(selection.getStart());
14206 				if (caretContainer) {
14207 					textNode = findFirstTextNode(caretContainer);
14208 				}
14209 
14210 				// Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
14211 				if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
14212 					// Get bookmark of caret position
14213 					bookmark = selection.getBookmark();
14214 
14215 					// Collapse bookmark range (WebKit)
14216 					rng.collapse(true);
14217 
14218 					// Expand the range to the closest word and split it at those points
14219 					rng = expandRng(rng, get(name));
14220 					rng = rangeUtils.split(rng);
14221 
14222 					// Apply the format to the range
14223 					apply(name, vars, rng);
14224 
14225 					// Move selection back to caret position
14226 					selection.moveToBookmark(bookmark);
14227 				} else {
14228 					if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
14229 						caretContainer = createCaretContainer(true);
14230 						textNode = caretContainer.firstChild;
14231 
14232 						rng.insertNode(caretContainer);
14233 						offset = 1;
14234 
14235 						apply(name, vars, caretContainer);
14236 					} else {
14237 						apply(name, vars, caretContainer);
14238 					}
14239 
14240 					// Move selection to text node
14241 					selection.setCursorLocation(textNode, offset);
14242 				}
14243 			}
14244 
14245 			function removeCaretFormat() {
14246 				var rng = selection.getRng(true), container, offset, bookmark,
14247 					hasContentAfter, node, formatNode, parents = [], i, caretContainer;
14248 
14249 				container = rng.startContainer;
14250 				offset = rng.startOffset;
14251 				node = container;
14252 
14253 				if (container.nodeType == 3) {
14254 					if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {
14255 						hasContentAfter = true;
14256 					}
14257 
14258 					node = node.parentNode;
14259 				}
14260 
14261 				while (node) {
14262 					if (matchNode(node, name, vars)) {
14263 						formatNode = node;
14264 						break;
14265 					}
14266 
14267 					if (node.nextSibling) {
14268 						hasContentAfter = true;
14269 					}
14270 
14271 					parents.push(node);
14272 					node = node.parentNode;
14273 				}
14274 
14275 				// Node doesn't have the specified format
14276 				if (!formatNode) {
14277 					return;
14278 				}
14279 
14280 				// Is there contents after the caret then remove the format on the element
14281 				if (hasContentAfter) {
14282 					// Get bookmark of caret position
14283 					bookmark = selection.getBookmark();
14284 
14285 					// Collapse bookmark range (WebKit)
14286 					rng.collapse(true);
14287 
14288 					// Expand the range to the closest word and split it at those points
14289 					rng = expandRng(rng, get(name), true);
14290 					rng = rangeUtils.split(rng);
14291 
14292 					// Remove the format from the range
14293 					remove(name, vars, rng);
14294 
14295 					// Move selection back to caret position
14296 					selection.moveToBookmark(bookmark);
14297 				} else {
14298 					caretContainer = createCaretContainer();
14299 
14300 					node = caretContainer;
14301 					for (i = parents.length - 1; i >= 0; i--) {
14302 						node.appendChild(dom.clone(parents[i], false));
14303 						node = node.firstChild;
14304 					}
14305 
14306 					// Insert invisible character into inner most format element
14307 					node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
14308 					node = node.firstChild;
14309 
14310 					var block = dom.getParent(formatNode, isTextBlock);
14311 
14312 					if (block && dom.isEmpty(block)) {
14313 						// Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>
14314 						formatNode.parentNode.replaceChild(caretContainer, formatNode);
14315 					} else {
14316 						// Insert caret container after the formated node
14317 						dom.insertAfter(caretContainer, formatNode);
14318 					}
14319 
14320 					// Move selection to text node
14321 					selection.setCursorLocation(node, 1);
14322 
14323 					// If the formatNode is empty, we can remove it safely. 
14324 					if (dom.isEmpty(formatNode)) {
14325 						dom.remove(formatNode);
14326 					}
14327 				}
14328 			}
14329 
14330 			// Checks if the parent caret container node isn't empty if that is the case it
14331 			// will remove the bogus state on all children that isn't empty
14332 			function unmarkBogusCaretParents() {
14333 				var caretContainer;
14334 
14335 				caretContainer = getParentCaretContainer(selection.getStart());
14336 				if (caretContainer && !dom.isEmpty(caretContainer)) {
14337 					walk(caretContainer, function(node) {
14338 						if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
14339 							dom.setAttrib(node, 'data-mce-bogus', null);
14340 						}
14341 					}, 'childNodes');
14342 				}
14343 			}
14344 
14345 			// Only bind the caret events once
14346 			if (!ed._hasCaretEvents) {
14347 				// Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
14348 				markCaretContainersBogus = function() {
14349 					var nodes = [], i;
14350 
14351 					if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
14352 						// Mark children
14353 						i = nodes.length;
14354 						while (i--) {
14355 							dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
14356 						}
14357 					}
14358 				};
14359 
14360 				disableCaretContainer = function(e) {
14361 					var keyCode = e.keyCode;
14362 
14363 					removeCaretContainer();
14364 
14365 					// Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
14366 					if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
14367 						removeCaretContainer(getParentCaretContainer(selection.getStart()));
14368 					}
14369 
14370 					unmarkBogusCaretParents();
14371 				};
14372 
14373 				// Remove bogus state if they got filled by contents using editor.selection.setContent
14374 				ed.on('SetContent', function(e) {
14375 					if (e.selection) {
14376 						unmarkBogusCaretParents();
14377 					}
14378 				});
14379 				ed._hasCaretEvents = true;
14380 			}
14381 
14382 			// Do apply or remove caret format
14383 			if (type == "apply") {
14384 				applyCaretFormat();
14385 			} else {
14386 				removeCaretFormat();
14387 			}
14388 		}
14389 
14390 		/**
14391 		 * Moves the start to the first suitable text node.
14392 		 */
14393 		function moveStart(rng) {
14394 			var container = rng.startContainer,
14395 					offset = rng.startOffset, isAtEndOfText,
14396 					walker, node, nodes, tmpNode;
14397 
14398 			// Convert text node into index if possible
14399 			if (container.nodeType == 3 && offset >= container.nodeValue.length) {
14400 				// Get the parent container location and walk from there
14401 				offset = nodeIndex(container);
14402 				container = container.parentNode;
14403 				isAtEndOfText = true;
14404 			}
14405 
14406 			// Move startContainer/startOffset in to a suitable node
14407 			if (container.nodeType == 1) {
14408 				nodes = container.childNodes;
14409 				container = nodes[Math.min(offset, nodes.length - 1)];
14410 				walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
14411 
14412 				// If offset is at end of the parent node walk to the next one
14413 				if (offset > nodes.length - 1 || isAtEndOfText) {
14414 					walker.next();
14415 				}
14416 
14417 				for (node = walker.current(); node; node = walker.next()) {
14418 					if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
14419 						// IE has a "neat" feature where it moves the start node into the closest element
14420 						// we can avoid this by inserting an element before it and then remove it after we set the selection
14421 						tmpNode = dom.create('a', null, INVISIBLE_CHAR);
14422 						node.parentNode.insertBefore(tmpNode, node);
14423 
14424 						// Set selection and remove tmpNode
14425 						rng.setStart(node, 0);
14426 						selection.setRng(rng);
14427 						dom.remove(tmpNode);
14428 
14429 						return;
14430 					}
14431 				}
14432 			}
14433 		}
14434 	};
14435 });
14436 
14437 // Included from: js/tinymce/classes/UndoManager.js
14438 
14439 /**
14440  * UndoManager.js
14441  *
14442  * Copyright, Moxiecode Systems AB
14443  * Released under LGPL License.
14444  *
14445  * License: http://www.tinymce.com/license
14446  * Contributing: http://www.tinymce.com/contributing
14447  */
14448 
14449 /**
14450  * This class handles the undo/redo history levels for the editor. Since the build in undo/redo has major drawbacks a custom one was needed.
14451  *
14452  * @class tinymce.UndoManager
14453  */
14454 define("tinymce/UndoManager", [
14455 	"tinymce/Env",
14456 	"tinymce/util/Tools"
14457 ], function(Env, Tools) {
14458 	var trim = Tools.trim, trimContentRegExp;
14459 
14460 	trimContentRegExp = new RegExp([
14461 		'<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers
14462 		'<div[^>]+data-mce-bogus[^>]+><\\/div>', // Trim bogus divs like resize handles
14463 		'\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected
14464 	].join('|'), 'gi');
14465 
14466 	return function(editor) {
14467 		var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0;
14468 
14469 		// Returns a trimmed version of the current editor contents
14470 		function getContent() {
14471 			return trim(editor.getContent({format: 'raw', no_events: 1}).replace(trimContentRegExp, ''));
14472 		}
14473 
14474 		function addNonTypingUndoLevel(e) {
14475 			self.typing = false;
14476 			self.add({}, e);
14477 		}
14478 
14479 		// Add initial undo level when the editor is initialized
14480 		editor.on('init', function() {
14481 			self.add();
14482 		});
14483 
14484 		// Get position before an execCommand is processed
14485 		editor.on('BeforeExecCommand', function(e) {
14486 			var cmd = e.command;
14487 
14488 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
14489 				self.beforeChange();
14490 			}
14491 		});
14492 
14493 		// Add undo level after an execCommand call was made
14494 		editor.on('ExecCommand', function(e) {
14495 			var cmd = e.command;
14496 
14497 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
14498 				addNonTypingUndoLevel(e);
14499 			}
14500 		});
14501 
14502 		editor.on('ObjectResizeStart', function() {
14503 			self.beforeChange();
14504 		});
14505 
14506 		editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel);
14507 		editor.on('DragEnd', addNonTypingUndoLevel);
14508 
14509 		editor.on('KeyUp', function(e) {
14510 			var keyCode = e.keyCode;
14511 
14512 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
14513 				addNonTypingUndoLevel();
14514 				editor.nodeChanged();
14515 			}
14516 
14517 			if (keyCode == 46 || keyCode == 8 || (Env.mac && (keyCode == 91 || keyCode == 93))) {
14518 				editor.nodeChanged();
14519 			}
14520 
14521 			// Fire a TypingUndo event on the first character entered
14522 			if (isFirstTypedCharacter && self.typing) {
14523 				// Make the it dirty if the content was changed after typing the first character
14524 				if (!editor.isDirty()) {
14525 					editor.isNotDirty = !data[0] || getContent() == data[0].content;
14526 
14527 					// Fire initial change event
14528 					if (!editor.isNotDirty) {
14529 						editor.fire('change', {level: data[0], lastLevel: null});
14530 					}
14531 				}
14532 
14533 				editor.fire('TypingUndo');
14534 				isFirstTypedCharacter = false;
14535 				editor.nodeChanged();
14536 			}
14537 		});
14538 
14539 		editor.on('KeyDown', function(e) {
14540 			var keyCode = e.keyCode;
14541 
14542 			// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
14543 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
14544 				if (self.typing) {
14545 					addNonTypingUndoLevel(e);
14546 				}
14547 
14548 				return;
14549 			}
14550 
14551 			// If key isn't shift,ctrl,alt,capslock,metakey
14552 			if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {
14553 				self.beforeChange();
14554 				self.typing = true;
14555 				self.add({}, e);
14556 				isFirstTypedCharacter = true;
14557 			}
14558 		});
14559 
14560 		editor.on('MouseDown', function(e) {
14561 			if (self.typing) {
14562 				addNonTypingUndoLevel(e);
14563 			}
14564 		});
14565 
14566 		// Add keyboard shortcuts for undo/redo keys
14567 		editor.addShortcut('ctrl+z', '', 'Undo');
14568 		editor.addShortcut('ctrl+y,ctrl+shift+z', '', 'Redo');
14569 
14570 		editor.on('AddUndo Undo Redo ClearUndos MouseUp', function(e) {
14571 			if (!e.isDefaultPrevented()) {
14572 				editor.nodeChanged();
14573 			}
14574 		});
14575 
14576 		self = {
14577 			// Explose for debugging reasons
14578 			data: data,
14579 
14580 			/**
14581 			 * State if the user is currently typing or not. This will add a typing operation into one undo
14582 			 * level instead of one new level for each keystroke.
14583 			 *
14584 			 * @field {Boolean} typing
14585 			 */
14586 			typing: false,
14587 
14588 			/**
14589 			 * Stores away a bookmark to be used when performing an undo action so that the selection is before
14590 			 * the change has been made.
14591 			 *
14592 			 * @method beforeChange
14593 			 */
14594 			beforeChange: function() {
14595 				if (!locks) {
14596 					beforeBookmark = editor.selection.getBookmark(2, true);
14597 				}
14598 			},
14599 
14600 			/**
14601 			 * Adds a new undo level/snapshot to the undo list.
14602 			 *
14603 			 * @method add
14604 			 * @param {Object} level Optional undo level object to add.
14605 			 * @param {DOMEvent} Event Optional event responsible for the creation of the undo level.
14606 			 * @return {Object} Undo level that got added or null it a level wasn't needed.
14607 			 */
14608 			add: function(level, event) {
14609 				var i, settings = editor.settings, lastLevel;
14610 
14611 				level = level || {};
14612 				level.content = getContent();
14613 
14614 				if (locks || editor.removed) {
14615 					return null;
14616 				}
14617 
14618 				lastLevel = data[index];
14619 				if (editor.fire('BeforeAddUndo', {level: level, lastLevel: lastLevel, originalEvent: event}).isDefaultPrevented()) {
14620 					return null;
14621 				}
14622 
14623 				// Add undo level if needed
14624 				if (lastLevel && lastLevel.content == level.content) {
14625 					return null;
14626 				}
14627 
14628 				// Set before bookmark on previous level
14629 				if (data[index]) {
14630 					data[index].beforeBookmark = beforeBookmark;
14631 				}
14632 
14633 				// Time to compress
14634 				if (settings.custom_undo_redo_levels) {
14635 					if (data.length > settings.custom_undo_redo_levels) {
14636 						for (i = 0; i < data.length - 1; i++) {
14637 							data[i] = data[i + 1];
14638 						}
14639 
14640 						data.length--;
14641 						index = data.length;
14642 					}
14643 				}
14644 
14645 				// Get a non intrusive normalized bookmark
14646 				level.bookmark = editor.selection.getBookmark(2, true);
14647 
14648 				// Crop array if needed
14649 				if (index < data.length - 1) {
14650 					data.length = index + 1;
14651 				}
14652 
14653 				data.push(level);
14654 				index = data.length - 1;
14655 
14656 				var args = {level: level, lastLevel: lastLevel, originalEvent: event};
14657 
14658 				editor.fire('AddUndo', args);
14659 
14660 				if (index > 0) {
14661 					editor.isNotDirty = false;
14662 					editor.fire('change', args);
14663 				}
14664 
14665 				return level;
14666 			},
14667 
14668 			/**
14669 			 * Undoes the last action.
14670 			 *
14671 			 * @method undo
14672 			 * @return {Object} Undo level or null if no undo was performed.
14673 			 */
14674 			undo: function() {
14675 				var level;
14676 
14677 				if (self.typing) {
14678 					self.add();
14679 					self.typing = false;
14680 				}
14681 
14682 				if (index > 0) {
14683 					level = data[--index];
14684 
14685 					// Undo to first index then set dirty state to false
14686 					if (index === 0) {
14687 						editor.isNotDirty = true;
14688 					}
14689 
14690 					editor.setContent(level.content, {format: 'raw'});
14691 					editor.selection.moveToBookmark(level.beforeBookmark);
14692 
14693 					editor.fire('undo', {level: level});
14694 				}
14695 
14696 				return level;
14697 			},
14698 
14699 			/**
14700 			 * Redoes the last action.
14701 			 *
14702 			 * @method redo
14703 			 * @return {Object} Redo level or null if no redo was performed.
14704 			 */
14705 			redo: function() {
14706 				var level;
14707 
14708 				if (index < data.length - 1) {
14709 					level = data[++index];
14710 
14711 					editor.setContent(level.content, {format: 'raw'});
14712 					editor.selection.moveToBookmark(level.bookmark);
14713 
14714 					editor.fire('redo', {level: level});
14715 				}
14716 
14717 				return level;
14718 			},
14719 
14720 			/**
14721 			 * Removes all undo levels.
14722 			 *
14723 			 * @method clear
14724 			 */
14725 			clear: function() {
14726 				data = [];
14727 				index = 0;
14728 				self.typing = false;
14729 				editor.fire('ClearUndos');
14730 			},
14731 
14732 			/**
14733 			 * Returns true/false if the undo manager has any undo levels.
14734 			 *
14735 			 * @method hasUndo
14736 			 * @return {Boolean} true/false if the undo manager has any undo levels.
14737 			 */
14738 			hasUndo: function() {
14739 				// Has undo levels or typing and content isn't the same as the initial level
14740 				return index > 0 || (self.typing && data[0] && getContent() != data[0].content);
14741 			},
14742 
14743 			/**
14744 			 * Returns true/false if the undo manager has any redo levels.
14745 			 *
14746 			 * @method hasRedo
14747 			 * @return {Boolean} true/false if the undo manager has any redo levels.
14748 			 */
14749 			hasRedo: function() {
14750 				return index < data.length - 1 && !this.typing;
14751 			},
14752 
14753 			/**
14754 			 * Executes the specified function in an undo transation. The selection
14755 			 * before the modification will be stored to the undo stack and if the DOM changes
14756 			 * it will add a new undo level. Any methods within the transation that adds undo levels will
14757 			 * be ignored. So a transation can include calls to execCommand or editor.insertContent.
14758 			 *
14759 			 * @method transact
14760 			 * @param {function} callback Function to execute dom manipulation logic in.
14761 			 */
14762 			transact: function(callback) {
14763 				self.beforeChange();
14764 
14765 				try {
14766 					locks++;
14767 					callback();
14768 				} finally {
14769 					locks--;
14770 				}
14771 
14772 				self.add();
14773 			}
14774 		};
14775 
14776 		return self;
14777 	};
14778 });
14779 
14780 // Included from: js/tinymce/classes/EnterKey.js
14781 
14782 /**
14783  * EnterKey.js
14784  *
14785  * Copyright, Moxiecode Systems AB
14786  * Released under LGPL License.
14787  *
14788  * License: http://www.tinymce.com/license
14789  * Contributing: http://www.tinymce.com/contributing
14790  */
14791 
14792 /**
14793  * Contains logic for handling the enter key to split/generate block elements.
14794  */
14795 define("tinymce/EnterKey", [
14796 	"tinymce/dom/TreeWalker",
14797 	"tinymce/dom/RangeUtils",
14798 	"tinymce/Env"
14799 ], function(TreeWalker, RangeUtils, Env) {
14800 	var isIE = Env.ie && Env.ie < 11;
14801 
14802 	return function(editor) {
14803 		var dom = editor.dom, selection = editor.selection, settings = editor.settings;
14804 		var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements();
14805 
14806 		function handleEnterKey(evt) {
14807 			var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
14808 				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
14809 
14810 			// Returns true if the block can be split into two blocks or not
14811 			function canSplitBlock(node) {
14812 				return node &&
14813 					dom.isBlock(node) &&
14814 					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
14815 					!/^(fixed|absolute)/i.test(node.style.position) &&
14816 					dom.getContentEditable(node) !== "true";
14817 			}
14818 
14819 			// Renders empty block on IE
14820 			function renderBlockOnIE(block) {
14821 				var oldRng;
14822 
14823 				if (dom.isBlock(block)) {
14824 					oldRng = selection.getRng();
14825 					block.appendChild(dom.create('span', null, '\u00a0'));
14826 					selection.select(block);
14827 					block.lastChild.outerHTML = '';
14828 					selection.setRng(oldRng);
14829 				}
14830 			}
14831 
14832 			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
14833 			function trimInlineElementsOnLeftSideOfBlock(block) {
14834 				var node = block, firstChilds = [], i;
14835 
14836 				// Find inner most first child ex: <p><i><b>*</b></i></p>
14837 				while ((node = node.firstChild)) {
14838 					if (dom.isBlock(node)) {
14839 						return;
14840 					}
14841 
14842 					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
14843 						firstChilds.push(node);
14844 					}
14845 				}
14846 
14847 				i = firstChilds.length;
14848 				while (i--) {
14849 					node = firstChilds[i];
14850 					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
14851 						dom.remove(node);
14852 					} else {
14853 						// Remove <a> </a> see #5381
14854 						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
14855 							dom.remove(node);
14856 						}
14857 					}
14858 				}
14859 			}
14860 
14861 			// Moves the caret to a suitable position within the root for example in the first non
14862 			// pure whitespace text node or before an image
14863 			function moveToCaretPosition(root) {
14864 				var walker, node, rng, lastNode = root, tempElm;
14865 
14866 				function firstNonWhiteSpaceNodeSibling(node) {
14867 					while (node) {
14868 						if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) {
14869 							return node;
14870 						}
14871 
14872 						node = node.nextSibling;
14873 					}
14874 				}
14875 
14876 				// Old IE versions doesn't properly render blocks with br elements in them
14877 				// For example <p><br></p> wont be rendered correctly in a contentEditable area
14878 				// until you remove the br producing <p></p>
14879 				if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
14880 					if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
14881 						dom.remove(parentBlock.firstChild);
14882 					}
14883 				}
14884 
14885 				if (root.nodeName == 'LI') {
14886 					var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
14887 
14888 					if (firstChild && /^(UL|OL)$/.test(firstChild.nodeName)) {
14889 						root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
14890 					}
14891 				}
14892 
14893 				rng = dom.createRng();
14894 
14895 				if (root.hasChildNodes()) {
14896 					walker = new TreeWalker(root, root);
14897 
14898 					while ((node = walker.current())) {
14899 						if (node.nodeType == 3) {
14900 							rng.setStart(node, 0);
14901 							rng.setEnd(node, 0);
14902 							break;
14903 						}
14904 
14905 						if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
14906 							rng.setStartBefore(node);
14907 							rng.setEndBefore(node);
14908 							break;
14909 						}
14910 
14911 						lastNode = node;
14912 						node = walker.next();
14913 					}
14914 
14915 					if (!node) {
14916 						rng.setStart(lastNode, 0);
14917 						rng.setEnd(lastNode, 0);
14918 					}
14919 				} else {
14920 					if (root.nodeName == 'BR') {
14921 						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
14922 							// Trick on older IE versions to render the caret before the BR between two lists
14923 							if (!documentMode || documentMode < 9) {
14924 								tempElm = dom.create('br');
14925 								root.parentNode.insertBefore(tempElm, root);
14926 							}
14927 
14928 							rng.setStartBefore(root);
14929 							rng.setEndBefore(root);
14930 						} else {
14931 							rng.setStartAfter(root);
14932 							rng.setEndAfter(root);
14933 						}
14934 					} else {
14935 						rng.setStart(root, 0);
14936 						rng.setEnd(root, 0);
14937 					}
14938 				}
14939 
14940 				selection.setRng(rng);
14941 
14942 				// Remove tempElm created for old IE:s
14943 				dom.remove(tempElm);
14944 				selection.scrollIntoView(root);
14945 			}
14946 
14947 			function setForcedBlockAttrs(node) {
14948 				var forcedRootBlockName = settings.forced_root_block;
14949 
14950 				if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
14951 					dom.setAttribs(node, settings.forced_root_block_attrs);
14952 				}
14953 			}
14954 
14955 			// Creates a new block element by cloning the current one or creating a new one if the name is specified
14956 			// This function will also copy any text formatting from the parent block and add it to the new one
14957 			function createNewBlock(name) {
14958 				var node = container, block, clonedNode, caretNode;
14959 
14960 				if (name || parentBlockName == "TABLE") {
14961 					block = dom.create(name || newBlockName);
14962 					setForcedBlockAttrs(block);
14963 				} else {
14964 					block = parentBlock.cloneNode(false);
14965 				}
14966 
14967 				caretNode = block;
14968 
14969 				// Clone any parent styles
14970 				if (settings.keep_styles !== false) {
14971 					do {
14972 						if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U|VAR|CITE|DFN|CODE|MARK|Q|SUP|SUB|SAMP)$/.test(node.nodeName)) {
14973 							// Never clone a caret containers
14974 							if (node.id == '_mce_caret') {
14975 								continue;
14976 							}
14977 
14978 							clonedNode = node.cloneNode(false);
14979 							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
14980 
14981 							if (block.hasChildNodes()) {
14982 								clonedNode.appendChild(block.firstChild);
14983 								block.appendChild(clonedNode);
14984 							} else {
14985 								caretNode = clonedNode;
14986 								block.appendChild(clonedNode);
14987 							}
14988 						}
14989 					} while ((node = node.parentNode));
14990 				}
14991 
14992 				// BR is needed in empty blocks on non IE browsers
14993 				if (!isIE) {
14994 					caretNode.innerHTML = '<br data-mce-bogus="1">';
14995 				}
14996 
14997 				return block;
14998 			}
14999 
15000 			// Returns true/false if the caret is at the start/end of the parent block element
15001 			function isCaretAtStartOrEndOfBlock(start) {
15002 				var walker, node, name;
15003 
15004 				// Caret is in the middle of a text node like "a|b"
15005 				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
15006 					return false;
15007 				}
15008 
15009 				// If after the last element in block node edge case for #5091
15010 				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
15011 					return true;
15012 				}
15013 
15014 				// If the caret if before the first element in parentBlock
15015 				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
15016 					return true;
15017 				}
15018 
15019 				// Caret can be before/after a table
15020 				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
15021 					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
15022 				}
15023 
15024 				// Walk the DOM and look for text nodes or non empty elements
15025 				walker = new TreeWalker(container, parentBlock);
15026 
15027 				// If caret is in beginning or end of a text block then jump to the next/previous node
15028 				if (container.nodeType == 3) {
15029 					if (start && offset === 0) {
15030 						walker.prev();
15031 					} else if (!start && offset == container.nodeValue.length) {
15032 						walker.next();
15033 					}
15034 				}
15035 
15036 				while ((node = walker.current())) {
15037 					if (node.nodeType === 1) {
15038 						// Ignore bogus elements
15039 						if (!node.getAttribute('data-mce-bogus')) {
15040 							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
15041 							name = node.nodeName.toLowerCase();
15042 							if (nonEmptyElementsMap[name] && name !== 'br') {
15043 								return false;
15044 							}
15045 						}
15046 					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
15047 						return false;
15048 					}
15049 
15050 					if (start) {
15051 						walker.prev();
15052 					} else {
15053 						walker.next();
15054 					}
15055 				}
15056 
15057 				return true;
15058 			}
15059 
15060 			// Wraps any text nodes or inline elements in the specified forced root block name
15061 			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
15062 				var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
15063 
15064 				// Not in a block element or in a table cell or caption
15065 				parentBlock = dom.getParent(container, dom.isBlock);
15066 				rootBlockName = editor.getBody().nodeName.toLowerCase();
15067 				if (!parentBlock || !canSplitBlock(parentBlock)) {
15068 					parentBlock = parentBlock || editableRoot;
15069 
15070 					if (!parentBlock.hasChildNodes()) {
15071 						newBlock = dom.create(blockName);
15072 						setForcedBlockAttrs(newBlock);
15073 						parentBlock.appendChild(newBlock);
15074 						rng.setStart(newBlock, 0);
15075 						rng.setEnd(newBlock, 0);
15076 						return newBlock;
15077 					}
15078 
15079 					// Find parent that is the first child of parentBlock
15080 					node = container;
15081 					while (node.parentNode != parentBlock) {
15082 						node = node.parentNode;
15083 					}
15084 
15085 					// Loop left to find start node start wrapping at
15086 					while (node && !dom.isBlock(node)) {
15087 						startNode = node;
15088 						node = node.previousSibling;
15089 					}
15090 
15091 					if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
15092 						newBlock = dom.create(blockName);
15093 						setForcedBlockAttrs(newBlock);
15094 						startNode.parentNode.insertBefore(newBlock, startNode);
15095 
15096 						// Start wrapping until we hit a block
15097 						node = startNode;
15098 						while (node && !dom.isBlock(node)) {
15099 							next = node.nextSibling;
15100 							newBlock.appendChild(node);
15101 							node = next;
15102 						}
15103 
15104 						// Restore range to it's past location
15105 						rng.setStart(container, offset);
15106 						rng.setEnd(container, offset);
15107 					}
15108 				}
15109 
15110 				return container;
15111 			}
15112 
15113 			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
15114 			function handleEmptyListItem() {
15115 				function isFirstOrLastLi(first) {
15116 					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
15117 
15118 					// Find first/last element since there might be whitespace there
15119 					while (node) {
15120 						if (node.nodeType == 1) {
15121 							break;
15122 						}
15123 
15124 						node = node[first ? 'nextSibling' : 'previousSibling'];
15125 					}
15126 
15127 					return node === parentBlock;
15128 				}
15129 
15130 				function getContainerBlock() {
15131 					var containerBlockParent = containerBlock.parentNode;
15132 
15133 					if (containerBlockParent.nodeName == 'LI') {
15134 						return containerBlockParent;
15135 					}
15136 
15137 					return containerBlock;
15138 				}
15139 
15140 				// Check if we are in an nested list
15141 				var containerBlockParentName = containerBlock.parentNode.nodeName;
15142 				if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
15143 					newBlockName = 'LI';
15144 				}
15145 
15146 				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
15147 
15148 				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
15149 					if (containerBlockParentName == 'LI') {
15150 						// Nested list is inside a LI
15151 						dom.insertAfter(newBlock, getContainerBlock());
15152 					} else {
15153 						// Is first and last list item then replace the OL/UL with a text block
15154 						dom.replace(newBlock, containerBlock);
15155 					}
15156 				} else if (isFirstOrLastLi(true)) {
15157 					if (containerBlockParentName == 'LI') {
15158 						// List nested in an LI then move the list to a new sibling LI
15159 						dom.insertAfter(newBlock, getContainerBlock());
15160 						newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
15161 						newBlock.appendChild(containerBlock);
15162 					} else {
15163 						// First LI in list then remove LI and add text block before list
15164 						containerBlock.parentNode.insertBefore(newBlock, containerBlock);
15165 					}
15166 				} else if (isFirstOrLastLi()) {
15167 					// Last LI in list then remove LI and add text block after list
15168 					dom.insertAfter(newBlock, getContainerBlock());
15169 					renderBlockOnIE(newBlock);
15170 				} else {
15171 					// Middle LI in list the split the list and insert a text block in the middle
15172 					// Extract after fragment and insert it after the current block
15173 					containerBlock = getContainerBlock();
15174 					tmpRng = rng.cloneRange();
15175 					tmpRng.setStartAfter(parentBlock);
15176 					tmpRng.setEndAfter(containerBlock);
15177 					fragment = tmpRng.extractContents();
15178 
15179 					if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') {
15180 						newBlock = fragment.firstChild;
15181 						dom.insertAfter(fragment, containerBlock);
15182 					} else {
15183 						dom.insertAfter(fragment, containerBlock);
15184 						dom.insertAfter(newBlock, containerBlock);
15185 					}
15186 				}
15187 
15188 				dom.remove(parentBlock);
15189 				moveToCaretPosition(newBlock);
15190 				undoManager.add();
15191 			}
15192 
15193 			// Walks the parent block to the right and look for BR elements
15194 			function hasRightSideContent() {
15195 				var walker = new TreeWalker(container, parentBlock), node;
15196 
15197 				while ((node = walker.next())) {
15198 					if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
15199 						return true;
15200 					}
15201 				}
15202 			}
15203 
15204 			// Inserts a BR element if the forced_root_block option is set to false or empty string
15205 			function insertBr() {
15206 				var brElm, extraBr, marker;
15207 
15208 				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
15209 					// Insert extra BR element at the end block elements
15210 					if (!isIE && !hasRightSideContent()) {
15211 						brElm = dom.create('br');
15212 						rng.insertNode(brElm);
15213 						rng.setStartAfter(brElm);
15214 						rng.setEndAfter(brElm);
15215 						extraBr = true;
15216 					}
15217 				}
15218 
15219 				brElm = dom.create('br');
15220 				rng.insertNode(brElm);
15221 
15222 				// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
15223 				if (isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
15224 					brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
15225 				}
15226 
15227 				// Insert temp marker and scroll to that
15228 				marker = dom.create('span', {}, ' ');
15229 				brElm.parentNode.insertBefore(marker, brElm);
15230 				selection.scrollIntoView(marker);
15231 				dom.remove(marker);
15232 
15233 				if (!extraBr) {
15234 					rng.setStartAfter(brElm);
15235 					rng.setEndAfter(brElm);
15236 				} else {
15237 					rng.setStartBefore(brElm);
15238 					rng.setEndBefore(brElm);
15239 				}
15240 
15241 				selection.setRng(rng);
15242 				undoManager.add();
15243 			}
15244 
15245 			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
15246 			function trimLeadingLineBreaks(node) {
15247 				do {
15248 					if (node.nodeType === 3) {
15249 						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
15250 					}
15251 
15252 					node = node.firstChild;
15253 				} while (node);
15254 			}
15255 
15256 			function getEditableRoot(node) {
15257 				var root = dom.getRoot(), parent, editableRoot;
15258 
15259 				// Get all parents until we hit a non editable parent or the root
15260 				parent = node;
15261 				while (parent !== root && dom.getContentEditable(parent) !== "false") {
15262 					if (dom.getContentEditable(parent) === "true") {
15263 						editableRoot = parent;
15264 					}
15265 
15266 					parent = parent.parentNode;
15267 				}
15268 
15269 				return parent !== root ? editableRoot : root;
15270 			}
15271 
15272 			// Adds a BR at the end of blocks that only contains an IMG or INPUT since
15273 			// these might be floated and then they won't expand the block
15274 			function addBrToBlockIfNeeded(block) {
15275 				var lastChild;
15276 
15277 				// IE will render the blocks correctly other browsers needs a BR
15278 				if (!isIE) {
15279 					block.normalize(); // Remove empty text nodes that got left behind by the extract
15280 
15281 					// Check if the block is empty or contains a floated last child
15282 					lastChild = block.lastChild;
15283 					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
15284 						dom.add(block, 'br');
15285 					}
15286 				}
15287 			}
15288 
15289 			rng = selection.getRng(true);
15290 
15291 			// Event is blocked by some other handler for example the lists plugin
15292 			if (evt.isDefaultPrevented()) {
15293 				return;
15294 			}
15295 
15296 			// Delete any selected contents
15297 			if (!rng.collapsed) {
15298 				editor.execCommand('Delete');
15299 				return;
15300 			}
15301 
15302 			// Setup range items and newBlockName
15303 			new RangeUtils(dom).normalize(rng);
15304 			container = rng.startContainer;
15305 			offset = rng.startOffset;
15306 			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
15307 			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
15308 			documentMode = dom.doc.documentMode;
15309 			shiftKey = evt.shiftKey;
15310 
15311 			// Resolve node index
15312 			if (container.nodeType == 1 && container.hasChildNodes()) {
15313 				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
15314 
15315 				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
15316 				if (isAfterLastNodeInContainer && container.nodeType == 3) {
15317 					offset = container.nodeValue.length;
15318 				} else {
15319 					offset = 0;
15320 				}
15321 			}
15322 
15323 			// Get editable root node normaly the body element but sometimes a div or span
15324 			editableRoot = getEditableRoot(container);
15325 
15326 			// If there is no editable root then enter is done inside a contentEditable false element
15327 			if (!editableRoot) {
15328 				return;
15329 			}
15330 
15331 			undoManager.beforeChange();
15332 
15333 			// If editable root isn't block nor the root of the editor
15334 			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
15335 				if (!newBlockName || shiftKey) {
15336 					insertBr();
15337 				}
15338 
15339 				return;
15340 			}
15341 
15342 			// Wrap the current node and it's sibling in a default block if it's needed.
15343 			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
15344 			// This won't happen if root blocks are disabled or the shiftKey is pressed
15345 			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
15346 				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
15347 			}
15348 
15349 			// Find parent block and setup empty block paddings
15350 			parentBlock = dom.getParent(container, dom.isBlock);
15351 			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
15352 
15353 			// Setup block names
15354 			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
15355 			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
15356 
15357 			// Enter inside block contained within a LI then split or insert before/after LI
15358 			if (containerBlockName == 'LI' && !evt.ctrlKey) {
15359 				parentBlock = containerBlock;
15360 				parentBlockName = containerBlockName;
15361 			}
15362 
15363 			// Handle enter in LI
15364 			if (parentBlockName == 'LI') {
15365 				if (!newBlockName && shiftKey) {
15366 					insertBr();
15367 					return;
15368 				}
15369 
15370 				// Handle enter inside an empty list item
15371 				if (dom.isEmpty(parentBlock)) {
15372 					handleEmptyListItem();
15373 					return;
15374 				}
15375 			}
15376 
15377 			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
15378 			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
15379 				if (!shiftKey) {
15380 					insertBr();
15381 					return;
15382 				}
15383 			} else {
15384 				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
15385 				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
15386 					insertBr();
15387 					return;
15388 				}
15389 			}
15390 
15391 			// If parent block is root then never insert new blocks
15392 			if (newBlockName && parentBlock === editor.getBody()) {
15393 				return;
15394 			}
15395 
15396 			// Default block name if it's not configured
15397 			newBlockName = newBlockName || 'P';
15398 
15399 			// Insert new block before/after the parent block depending on caret location
15400 			if (isCaretAtStartOrEndOfBlock()) {
15401 				// If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
15402 				if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
15403 					newBlock = createNewBlock(newBlockName);
15404 				} else {
15405 					newBlock = createNewBlock();
15406 				}
15407 
15408 				// Split the current container block element if enter is pressed inside an empty inner block element
15409 				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
15410 					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
15411 					newBlock = dom.split(containerBlock, parentBlock);
15412 				} else {
15413 					dom.insertAfter(newBlock, parentBlock);
15414 				}
15415 
15416 				moveToCaretPosition(newBlock);
15417 			} else if (isCaretAtStartOrEndOfBlock(true)) {
15418 				// Insert new block before
15419 				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
15420 				renderBlockOnIE(newBlock);
15421 				moveToCaretPosition(parentBlock);
15422 			} else {
15423 				// Extract after fragment and insert it after the current block
15424 				tmpRng = rng.cloneRange();
15425 				tmpRng.setEndAfter(parentBlock);
15426 				fragment = tmpRng.extractContents();
15427 				trimLeadingLineBreaks(fragment);
15428 				newBlock = fragment.firstChild;
15429 				dom.insertAfter(fragment, parentBlock);
15430 				trimInlineElementsOnLeftSideOfBlock(newBlock);
15431 				addBrToBlockIfNeeded(parentBlock);
15432 				moveToCaretPosition(newBlock);
15433 			}
15434 
15435 			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
15436 
15437 			// Allow custom handling of new blocks
15438 			editor.fire('NewBlock', { newBlock: newBlock });
15439 
15440 			undoManager.add();
15441 		}
15442 
15443 		editor.on('keydown', function(evt) {
15444 			if (evt.keyCode == 13) {
15445 				if (handleEnterKey(evt) !== false) {
15446 					evt.preventDefault();
15447 				}
15448 			}
15449 		});
15450 	};
15451 });
15452 
15453 // Included from: js/tinymce/classes/ForceBlocks.js
15454 
15455 /**
15456  * ForceBlocks.js
15457  *
15458  * Copyright, Moxiecode Systems AB
15459  * Released under LGPL License.
15460  *
15461  * License: http://www.tinymce.com/license
15462  * Contributing: http://www.tinymce.com/contributing
15463  */
15464 
15465 define("tinymce/ForceBlocks", [], function() {
15466 	return function(editor) {
15467 		var settings = editor.settings, dom = editor.dom, selection = editor.selection;
15468 		var schema = editor.schema, blockElements = schema.getBlockElements();
15469 
15470 		function addRootBlocks() {
15471 			var node = selection.getStart(), rootNode = editor.getBody(), rng;
15472 			var startContainer, startOffset, endContainer, endOffset, rootBlockNode;
15473 			var tempNode, offset = -0xFFFFFF, wrapped, restoreSelection;
15474 			var tmpRng, rootNodeName, forcedRootBlock;
15475 
15476 			forcedRootBlock = settings.forced_root_block;
15477 
15478 			if (!node || node.nodeType !== 1 || !forcedRootBlock) {
15479 				return;
15480 			}
15481 
15482 			// Check if node is wrapped in block
15483 			while (node && node != rootNode) {
15484 				if (blockElements[node.nodeName]) {
15485 					return;
15486 				}
15487 
15488 				node = node.parentNode;
15489 			}
15490 
15491 			// Get current selection
15492 			rng = selection.getRng();
15493 			if (rng.setStart) {
15494 				startContainer = rng.startContainer;
15495 				startOffset = rng.startOffset;
15496 				endContainer = rng.endContainer;
15497 				endOffset = rng.endOffset;
15498 
15499 				try {
15500 					restoreSelection = editor.getDoc().activeElement === rootNode;
15501 				} catch (ex) {
15502 					// IE throws unspecified error here sometimes
15503 				}
15504 			} else {
15505 				// Force control range into text range
15506 				if (rng.item) {
15507 					node = rng.item(0);
15508 					rng = editor.getDoc().body.createTextRange();
15509 					rng.moveToElementText(node);
15510 				}
15511 
15512 				restoreSelection = rng.parentElement().ownerDocument === editor.getDoc();
15513 				tmpRng = rng.duplicate();
15514 				tmpRng.collapse(true);
15515 				startOffset = tmpRng.move('character', offset) * -1;
15516 
15517 				if (!tmpRng.collapsed) {
15518 					tmpRng = rng.duplicate();
15519 					tmpRng.collapse(false);
15520 					endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
15521 				}
15522 			}
15523 
15524 			// Wrap non block elements and text nodes
15525 			node = rootNode.firstChild;
15526 			rootNodeName = rootNode.nodeName.toLowerCase();
15527 			while (node) {
15528 				// TODO: Break this up, too complex
15529 				if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) &&
15530 					schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) {
15531 					// Remove empty text nodes
15532 					if (node.nodeType === 3 && node.nodeValue.length === 0) {
15533 						tempNode = node;
15534 						node = node.nextSibling;
15535 						dom.remove(tempNode);
15536 						continue;
15537 					}
15538 
15539 					if (!rootBlockNode) {
15540 						rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs);
15541 						node.parentNode.insertBefore(rootBlockNode, node);
15542 						wrapped = true;
15543 					}
15544 
15545 					tempNode = node;
15546 					node = node.nextSibling;
15547 					rootBlockNode.appendChild(tempNode);
15548 				} else {
15549 					rootBlockNode = null;
15550 					node = node.nextSibling;
15551 				}
15552 			}
15553 
15554 			if (wrapped && restoreSelection) {
15555 				if (rng.setStart) {
15556 					rng.setStart(startContainer, startOffset);
15557 					rng.setEnd(endContainer, endOffset);
15558 					selection.setRng(rng);
15559 				} else {
15560 					// Only select if the previous selection was inside the document to prevent auto focus in quirks mode
15561 					try {
15562 						rng = editor.getDoc().body.createTextRange();
15563 						rng.moveToElementText(rootNode);
15564 						rng.collapse(true);
15565 						rng.moveStart('character', startOffset);
15566 
15567 						if (endOffset > 0) {
15568 							rng.moveEnd('character', endOffset);
15569 						}
15570 
15571 						rng.select();
15572 					} catch (ex) {
15573 						// Ignore
15574 					}
15575 				}
15576 
15577 				editor.nodeChanged();
15578 			}
15579 		}
15580 
15581 		// Force root blocks
15582 		if (settings.forced_root_block) {
15583 			editor.on('NodeChange', addRootBlocks);
15584 		}
15585 	};
15586 });
15587 
15588 // Included from: js/tinymce/classes/EditorCommands.js
15589 
15590 /**
15591  * EditorCommands.js
15592  *
15593  * Copyright, Moxiecode Systems AB
15594  * Released under LGPL License.
15595  *
15596  * License: http://www.tinymce.com/license
15597  * Contributing: http://www.tinymce.com/contributing
15598  */
15599 
15600 /**
15601  * This class enables you to add custom editor commands and it contains
15602  * overrides for native browser commands to address various bugs and issues.
15603  *
15604  * @class tinymce.EditorCommands
15605  */
15606 define("tinymce/EditorCommands", [
15607 	"tinymce/html/Serializer",
15608 	"tinymce/Env",
15609 	"tinymce/util/Tools"
15610 ], function(Serializer, Env, Tools) {
15611 	// Added for compression purposes
15612 	var each = Tools.each, extend = Tools.extend;
15613 	var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
15614 	var isGecko = Env.gecko, isIE = Env.ie;
15615 	var TRUE = true, FALSE = false;
15616 
15617 	return function(editor) {
15618 		var dom = editor.dom,
15619 			selection = editor.selection,
15620 			commands = {state: {}, exec: {}, value: {}},
15621 			settings = editor.settings,
15622 			formatter = editor.formatter,
15623 			bookmark;
15624 
15625 		/**
15626 		 * Executes the specified command.
15627 		 *
15628 		 * @method execCommand
15629 		 * @param {String} command Command to execute.
15630 		 * @param {Boolean} ui Optional user interface state.
15631 		 * @param {Object} value Optional value for command.
15632 		 * @return {Boolean} true/false if the command was found or not.
15633 		 */
15634 		function execCommand(command, ui, value) {
15635 			var func;
15636 
15637 			command = command.toLowerCase();
15638 			if ((func = commands.exec[command])) {
15639 				func(command, ui, value);
15640 				return TRUE;
15641 			}
15642 
15643 			return FALSE;
15644 		}
15645 
15646 		/**
15647 		 * Queries the current state for a command for example if the current selection is "bold".
15648 		 *
15649 		 * @method queryCommandState
15650 		 * @param {String} command Command to check the state of.
15651 		 * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found.
15652 		 */
15653 		function queryCommandState(command) {
15654 			var func;
15655 
15656 			command = command.toLowerCase();
15657 			if ((func = commands.state[command])) {
15658 				return func(command);
15659 			}
15660 
15661 			return -1;
15662 		}
15663 
15664 		/**
15665 		 * Queries the command value for example the current fontsize.
15666 		 *
15667 		 * @method queryCommandValue
15668 		 * @param {String} command Command to check the value of.
15669 		 * @return {Object} Command value of false if it's not found.
15670 		 */
15671 		function queryCommandValue(command) {
15672 			var func;
15673 
15674 			command = command.toLowerCase();
15675 			if ((func = commands.value[command])) {
15676 				return func(command);
15677 			}
15678 
15679 			return FALSE;
15680 		}
15681 
15682 		/**
15683 		 * Adds commands to the command collection.
15684 		 *
15685 		 * @method addCommands
15686 		 * @param {Object} command_list Name/value collection with commands to add, the names can also be comma separated.
15687 		 * @param {String} type Optional type to add, defaults to exec. Can be value or state as well.
15688 		 */
15689 		function addCommands(command_list, type) {
15690 			type = type || 'exec';
15691 
15692 			each(command_list, function(callback, command) {
15693 				each(command.toLowerCase().split(','), function(command) {
15694 					commands[type][command] = callback;
15695 				});
15696 			});
15697 		}
15698 
15699 		// Expose public methods
15700 		extend(this, {
15701 			execCommand: execCommand,
15702 			queryCommandState: queryCommandState,
15703 			queryCommandValue: queryCommandValue,
15704 			addCommands: addCommands
15705 		});
15706 
15707 		// Private methods
15708 
15709 		function execNativeCommand(command, ui, value) {
15710 			if (ui === undefined) {
15711 				ui = FALSE;
15712 			}
15713 
15714 			if (value === undefined) {
15715 				value = null;
15716 			}
15717 
15718 			return editor.getDoc().execCommand(command, ui, value);
15719 		}
15720 
15721 		function isFormatMatch(name) {
15722 			return formatter.match(name);
15723 		}
15724 
15725 		function toggleFormat(name, value) {
15726 			formatter.toggle(name, value ? {value: value} : undefined);
15727 			editor.nodeChanged();
15728 		}
15729 
15730 		function storeSelection(type) {
15731 			bookmark = selection.getBookmark(type);
15732 		}
15733 
15734 		function restoreSelection() {
15735 			selection.moveToBookmark(bookmark);
15736 		}
15737 
15738 		// Add execCommand overrides
15739 		addCommands({
15740 			// Ignore these, added for compatibility
15741 			'mceResetDesignMode,mceBeginUndoLevel': function() {},
15742 
15743 			// Add undo manager logic
15744 			'mceEndUndoLevel,mceAddUndoLevel': function() {
15745 				editor.undoManager.add();
15746 			},
15747 
15748 			'Cut,Copy,Paste': function(command) {
15749 				var doc = editor.getDoc(), failed;
15750 
15751 				// Try executing the native command
15752 				try {
15753 					execNativeCommand(command);
15754 				} catch (ex) {
15755 					// Command failed
15756 					failed = TRUE;
15757 				}
15758 
15759 				// Present alert message about clipboard access not being available
15760 				if (failed || !doc.queryCommandSupported(command)) {
15761 					var msg = editor.translate(
15762 						"Your browser doesn't support direct access to the clipboard. " +
15763 						"Please use the Ctrl+X/C/V keyboard shortcuts instead."
15764 					);
15765 
15766 					if (Env.mac) {
15767 						msg = msg.replace(/Ctrl\+/g, '\u2318+');
15768 					}
15769 
15770 					editor.windowManager.alert(msg);
15771 				}
15772 			},
15773 
15774 			// Override unlink command
15775 			unlink: function() {
15776 				if (selection.isCollapsed()) {
15777 					var elm = selection.getNode();
15778 					if (elm.tagName == 'A') {
15779 						editor.dom.remove(elm, true);
15780 					}
15781 
15782 					return;
15783 				}
15784 
15785 				formatter.remove("link");
15786 			},
15787 
15788 			// Override justify commands to use the text formatter engine
15789 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
15790 				var align = command.substring(7);
15791 
15792 				if (align == 'full') {
15793 					align = 'justify';
15794 				}
15795 
15796 				// Remove all other alignments first
15797 				each('left,center,right,justify'.split(','), function(name) {
15798 					if (align != name) {
15799 						formatter.remove('align' + name);
15800 					}
15801 				});
15802 
15803 				toggleFormat('align' + align);
15804 				execCommand('mceRepaint');
15805 			},
15806 
15807 			// Override list commands to fix WebKit bug
15808 			'InsertUnorderedList,InsertOrderedList': function(command) {
15809 				var listElm, listParent;
15810 
15811 				execNativeCommand(command);
15812 
15813 				// WebKit produces lists within block elements so we need to split them
15814 				// we will replace the native list creation logic to custom logic later on
15815 				// TODO: Remove this when the list creation logic is removed
15816 				listElm = dom.getParent(selection.getNode(), 'ol,ul');
15817 				if (listElm) {
15818 					listParent = listElm.parentNode;
15819 
15820 					// If list is within a text block then split that block
15821 					if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
15822 						storeSelection();
15823 						dom.split(listParent, listElm);
15824 						restoreSelection();
15825 					}
15826 				}
15827 			},
15828 
15829 			// Override commands to use the text formatter engine
15830 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
15831 				toggleFormat(command);
15832 			},
15833 
15834 			// Override commands to use the text formatter engine
15835 			'ForeColor,HiliteColor,FontName': function(command, ui, value) {
15836 				toggleFormat(command, value);
15837 			},
15838 
15839 			FontSize: function(command, ui, value) {
15840 				var fontClasses, fontSizes;
15841 
15842 				// Convert font size 1-7 to styles
15843 				if (value >= 1 && value <= 7) {
15844 					fontSizes = explode(settings.font_size_style_values);
15845 					fontClasses = explode(settings.font_size_classes);
15846 
15847 					if (fontClasses) {
15848 						value = fontClasses[value - 1] || value;
15849 					} else {
15850 						value = fontSizes[value - 1] || value;
15851 					}
15852 				}
15853 
15854 				toggleFormat(command, value);
15855 			},
15856 
15857 			RemoveFormat: function(command) {
15858 				formatter.remove(command);
15859 			},
15860 
15861 			mceBlockQuote: function() {
15862 				toggleFormat('blockquote');
15863 			},
15864 
15865 			FormatBlock: function(command, ui, value) {
15866 				return toggleFormat(value || 'p');
15867 			},
15868 
15869 			mceCleanup: function() {
15870 				var bookmark = selection.getBookmark();
15871 
15872 				editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE});
15873 
15874 				selection.moveToBookmark(bookmark);
15875 			},
15876 
15877 			mceRemoveNode: function(command, ui, value) {
15878 				var node = value || selection.getNode();
15879 
15880 				// Make sure that the body node isn't removed
15881 				if (node != editor.getBody()) {
15882 					storeSelection();
15883 					editor.dom.remove(node, TRUE);
15884 					restoreSelection();
15885 				}
15886 			},
15887 
15888 			mceSelectNodeDepth: function(command, ui, value) {
15889 				var counter = 0;
15890 
15891 				dom.getParent(selection.getNode(), function(node) {
15892 					if (node.nodeType == 1 && counter++ == value) {
15893 						selection.select(node);
15894 						return FALSE;
15895 					}
15896 				}, editor.getBody());
15897 			},
15898 
15899 			mceSelectNode: function(command, ui, value) {
15900 				selection.select(value);
15901 			},
15902 
15903 			mceInsertContent: function(command, ui, value) {
15904 				var parser, serializer, parentNode, rootNode, fragment, args;
15905 				var marker, rng, node, node2, bookmarkHtml;
15906 
15907 				function trimOrPaddLeftRight(html) {
15908 					var rng, container, offset;
15909 
15910 					rng = selection.getRng(true);
15911 					container = rng.startContainer;
15912 					offset = rng.startOffset;
15913 
15914 					function hasSiblingText(siblingName) {
15915 						return container[siblingName] && container[siblingName].nodeType == 3;
15916 					}
15917 
15918 					if (container.nodeType == 3) {
15919 						if (offset > 0) {
15920 							html = html.replace(/^ /, ' ');
15921 						} else if (!hasSiblingText('previousSibling')) {
15922 							html = html.replace(/^ /, ' ');
15923 						}
15924 
15925 						if (offset < container.length) {
15926 							html = html.replace(/ (<br>|)$/, ' ');
15927 						} else if (!hasSiblingText('nextSibling')) {
15928 							html = html.replace(/( | )(<br>|)$/, ' ');
15929 						}
15930 					}
15931 
15932 					return html;
15933 				}
15934 
15935 				// Check for whitespace before/after value
15936 				if (/^ | $/.test(value)) {
15937 					value = trimOrPaddLeftRight(value);
15938 				}
15939 
15940 				// Setup parser and serializer
15941 				parser = editor.parser;
15942 				serializer = new Serializer({}, editor.schema);
15943 				bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">ÈB;</span>';
15944 
15945 				// Run beforeSetContent handlers on the HTML to be inserted
15946 				args = {content: value, format: 'html', selection: true};
15947 				editor.fire('BeforeSetContent', args);
15948 				value = args.content;
15949 
15950 				// Add caret at end of contents if it's missing
15951 				if (value.indexOf('{$caret}') == -1) {
15952 					value += '{$caret}';
15953 				}
15954 
15955 				// Replace the caret marker with a span bookmark element
15956 				value = value.replace(/\{\$caret\}/, bookmarkHtml);
15957 
15958 				// If selection is at <body>|<p></p> then move it into <body><p>|</p>
15959 				rng = selection.getRng();
15960 				var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
15961 				var body = editor.getBody();
15962 				if (caretElement === body && selection.isCollapsed()) {
15963 					if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
15964 						rng = dom.createRng();
15965 						rng.setStart(body.firstChild, 0);
15966 						rng.setEnd(body.firstChild, 0);
15967 						selection.setRng(rng);
15968 					}
15969 				}
15970 
15971 				// Insert node maker where we will insert the new HTML and get it's parent
15972 				if (!selection.isCollapsed()) {
15973 					editor.getDoc().execCommand('Delete', false, null);
15974 				}
15975 
15976 				parentNode = selection.getNode();
15977 
15978 				// Parse the fragment within the context of the parent node
15979 				var parserArgs = {context: parentNode.nodeName.toLowerCase()};
15980 				fragment = parser.parse(value, parserArgs);
15981 
15982 				// Move the caret to a more suitable location
15983 				node = fragment.lastChild;
15984 				if (node.attr('id') == 'mce_marker') {
15985 					marker = node;
15986 
15987 					for (node = node.prev; node; node = node.walk(true)) {
15988 						if (node.type == 3 || !dom.isBlock(node.name)) {
15989 							node.parent.insert(marker, node, node.name === 'br');
15990 							break;
15991 						}
15992 					}
15993 				}
15994 
15995 				// If parser says valid we can insert the contents into that parent
15996 				if (!parserArgs.invalid) {
15997 					value = serializer.serialize(fragment);
15998 
15999 					// Check if parent is empty or only has one BR element then set the innerHTML of that parent
16000 					node = parentNode.firstChild;
16001 					node2 = parentNode.lastChild;
16002 					if (!node || (node === node2 && node.nodeName === 'BR')) {
16003 						dom.setHTML(parentNode, value);
16004 					} else {
16005 						selection.setContent(value);
16006 					}
16007 				} else {
16008 					// If the fragment was invalid within that context then we need
16009 					// to parse and process the parent it's inserted into
16010 
16011 					// Insert bookmark node and get the parent
16012 					selection.setContent(bookmarkHtml);
16013 					parentNode = selection.getNode();
16014 					rootNode = editor.getBody();
16015 
16016 					// Opera will return the document node when selection is in root
16017 					if (parentNode.nodeType == 9) {
16018 						parentNode = node = rootNode;
16019 					} else {
16020 						node = parentNode;
16021 					}
16022 
16023 					// Find the ancestor just before the root element
16024 					while (node !== rootNode) {
16025 						parentNode = node;
16026 						node = node.parentNode;
16027 					}
16028 
16029 					// Get the outer/inner HTML depending on if we are in the root and parser and serialize that
16030 					value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
16031 					value = serializer.serialize(
16032 						parser.parse(
16033 							// Need to replace by using a function since $ in the contents would otherwise be a problem
16034 							value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
16035 								return serializer.serialize(fragment);
16036 							})
16037 						)
16038 					);
16039 
16040 					// Set the inner/outer HTML depending on if we are in the root or not
16041 					if (parentNode == rootNode) {
16042 						dom.setHTML(rootNode, value);
16043 					} else {
16044 						dom.setOuterHTML(parentNode, value);
16045 					}
16046 				}
16047 
16048 				marker = dom.get('mce_marker');
16049 				selection.scrollIntoView(marker);
16050 
16051 				// Move selection before marker and remove it
16052 				rng = dom.createRng();
16053 
16054 				// If previous sibling is a text node set the selection to the end of that node
16055 				node = marker.previousSibling;
16056 				if (node && node.nodeType == 3) {
16057 					rng.setStart(node, node.nodeValue.length);
16058 
16059 					// TODO: Why can't we normalize on IE
16060 					if (!isIE) {
16061 						node2 = marker.nextSibling;
16062 						if (node2 && node2.nodeType == 3) {
16063 							node.appendData(node2.data);
16064 							node2.parentNode.removeChild(node2);
16065 						}
16066 					}
16067 				} else {
16068 					// If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
16069 					rng.setStartBefore(marker);
16070 					rng.setEndBefore(marker);
16071 				}
16072 
16073 				// Remove the marker node and set the new range
16074 				dom.remove(marker);
16075 				selection.setRng(rng);
16076 
16077 				// Dispatch after event and add any visual elements needed
16078 				editor.fire('SetContent', args);
16079 				editor.addVisual();
16080 			},
16081 
16082 			mceInsertRawHTML: function(command, ui, value) {
16083 				selection.setContent('tiny_mce_marker');
16084 				editor.setContent(
16085 					editor.getContent().replace(/tiny_mce_marker/g, function() {
16086 						return value;
16087 					})
16088 				);
16089 			},
16090 
16091 			mceToggleFormat: function(command, ui, value) {
16092 				toggleFormat(value);
16093 			},
16094 
16095 			mceSetContent: function(command, ui, value) {
16096 				editor.setContent(value);
16097 			},
16098 
16099 			'Indent,Outdent': function(command) {
16100 				var intentValue, indentUnit, value;
16101 
16102 				// Setup indent level
16103 				intentValue = settings.indentation;
16104 				indentUnit = /[a-z%]+$/i.exec(intentValue);
16105 				intentValue = parseInt(intentValue, 10);
16106 
16107 				if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
16108 					// If forced_root_blocks is set to false we don't have a block to indent so lets create a div
16109 					if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
16110 						formatter.apply('div');
16111 					}
16112 
16113 					each(selection.getSelectedBlocks(), function(element) {
16114 						if (element.nodeName != "LI") {
16115 							var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding';
16116 
16117 							indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left';
16118 
16119 							if (command == 'outdent') {
16120 								value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue);
16121 								dom.setStyle(element, indentStyleName, value ? value + indentUnit : '');
16122 							} else {
16123 								value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit;
16124 								dom.setStyle(element, indentStyleName, value);
16125 							}
16126 						}
16127 					});
16128 				} else {
16129 					execNativeCommand(command);
16130 				}
16131 			},
16132 
16133 			mceRepaint: function() {
16134 				if (isGecko) {
16135 					try {
16136 						storeSelection(TRUE);
16137 
16138 						if (selection.getSel()) {
16139 							selection.getSel().selectAllChildren(editor.getBody());
16140 						}
16141 
16142 						selection.collapse(TRUE);
16143 						restoreSelection();
16144 					} catch (ex) {
16145 						// Ignore
16146 					}
16147 				}
16148 			},
16149 
16150 			InsertHorizontalRule: function() {
16151 				editor.execCommand('mceInsertContent', false, '<hr />');
16152 			},
16153 
16154 			mceToggleVisualAid: function() {
16155 				editor.hasVisual = !editor.hasVisual;
16156 				editor.addVisual();
16157 			},
16158 
16159 			mceReplaceContent: function(command, ui, value) {
16160 				editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'})));
16161 			},
16162 
16163 			mceInsertLink: function(command, ui, value) {
16164 				var anchor;
16165 
16166 				if (typeof(value) == 'string') {
16167 					value = {href: value};
16168 				}
16169 
16170 				anchor = dom.getParent(selection.getNode(), 'a');
16171 
16172 				// Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
16173 				value.href = value.href.replace(' ', '%20');
16174 
16175 				// Remove existing links if there could be child links or that the href isn't specified
16176 				if (!anchor || !value.href) {
16177 					formatter.remove('link');
16178 				}
16179 
16180 				// Apply new link to selection
16181 				if (value.href) {
16182 					formatter.apply('link', value, anchor);
16183 				}
16184 			},
16185 
16186 			selectAll: function() {
16187 				var root = dom.getRoot(), rng;
16188 
16189 				if (selection.getRng().setStart) {
16190 					rng = dom.createRng();
16191 					rng.setStart(root, 0);
16192 					rng.setEnd(root, root.childNodes.length);
16193 					selection.setRng(rng);
16194 				} else {
16195 					// IE will render it's own root level block elements and sometimes
16196 					// even put font elements in them when the user starts typing. So we need to
16197 					// move the selection to a more suitable element from this:
16198 					// <body>|<p></p></body> to this: <body><p>|</p></body>
16199 					rng = selection.getRng();
16200 					if (!rng.item) {
16201 						rng.moveToElementText(root);
16202 						rng.select();
16203 					}
16204 				}
16205 			},
16206 
16207 			"delete": function() {
16208 				execNativeCommand("Delete");
16209 
16210 				// Check if body is empty after the delete call if so then set the contents
16211 				// to an empty string and move the caret to any block produced by that operation
16212 				// this fixes the issue with root blocks not being properly produced after a delete call on IE
16213 				var body = editor.getBody();
16214 
16215 				if (dom.isEmpty(body)) {
16216 					editor.setContent('');
16217 
16218 					if (body.firstChild && dom.isBlock(body.firstChild)) {
16219 						editor.selection.setCursorLocation(body.firstChild, 0);
16220 					} else {
16221 						editor.selection.setCursorLocation(body, 0);
16222 					}
16223 				}
16224 			},
16225 
16226 			mceNewDocument: function() {
16227 				editor.setContent('');
16228 			}
16229 		});
16230 
16231 		// Add queryCommandState overrides
16232 		addCommands({
16233 			// Override justify commands
16234 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
16235 				var name = 'align' + command.substring(7);
16236 				var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
16237 				var matches = map(nodes, function(node) {
16238 					return !!formatter.matchNode(node, name);
16239 				});
16240 				return inArray(matches, TRUE) !== -1;
16241 			},
16242 
16243 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
16244 				return isFormatMatch(command);
16245 			},
16246 
16247 			mceBlockQuote: function() {
16248 				return isFormatMatch('blockquote');
16249 			},
16250 
16251 			Outdent: function() {
16252 				var node;
16253 
16254 				if (settings.inline_styles) {
16255 					if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
16256 						return TRUE;
16257 					}
16258 
16259 					if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
16260 						return TRUE;
16261 					}
16262 				}
16263 
16264 				return (
16265 					queryCommandState('InsertUnorderedList') ||
16266 					queryCommandState('InsertOrderedList') ||
16267 					(!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'))
16268 				);
16269 			},
16270 
16271 			'InsertUnorderedList,InsertOrderedList': function(command) {
16272 				var list = dom.getParent(selection.getNode(), 'ul,ol');
16273 
16274 				return list &&
16275 					(
16276 						command === 'insertunorderedlist' && list.tagName === 'UL' ||
16277 						command === 'insertorderedlist' && list.tagName === 'OL'
16278 					);
16279 			}
16280 		}, 'state');
16281 
16282 		// Add queryCommandValue overrides
16283 		addCommands({
16284 			'FontSize,FontName': function(command) {
16285 				var value = 0, parent;
16286 
16287 				if ((parent = dom.getParent(selection.getNode(), 'span'))) {
16288 					if (command == 'fontsize') {
16289 						value = parent.style.fontSize;
16290 					} else {
16291 						value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
16292 					}
16293 				}
16294 
16295 				return value;
16296 			}
16297 		}, 'value');
16298 
16299 		// Add undo manager logic
16300 		addCommands({
16301 			Undo: function() {
16302 				editor.undoManager.undo();
16303 			},
16304 
16305 			Redo: function() {
16306 				editor.undoManager.redo();
16307 			}
16308 		});
16309 	};
16310 });
16311 
16312 // Included from: js/tinymce/classes/util/URI.js
16313 
16314 /**
16315  * URI.js
16316  *
16317  * Copyright, Moxiecode Systems AB
16318  * Released under LGPL License.
16319  *
16320  * License: http://www.tinymce.com/license
16321  * Contributing: http://www.tinymce.com/contributing
16322  */
16323 
16324 /**
16325  * This class handles parsing, modification and serialization of URI/URL strings.
16326  * @class tinymce.util.URI
16327  */
16328 define("tinymce/util/URI", [
16329 	"tinymce/util/Tools"
16330 ], function(Tools) {
16331 	var each = Tools.each, trim = Tools.trim,
16332 		DEFAULT_PORTS = {
16333 			'ftp': 21,
16334 			'http': 80,
16335 			'https': 443,
16336 			'mailto': 25
16337 		};
16338 
16339 	/**
16340 	 * Constructs a new URI instance.
16341 	 *
16342 	 * @constructor
16343 	 * @method URI
16344 	 * @param {String} url URI string to parse.
16345 	 * @param {Object} settings Optional settings object.
16346 	 */
16347 	function URI(url, settings) {
16348 		var self = this, baseUri, base_url;
16349 
16350 		// Trim whitespace
16351 		url = trim(url);
16352 
16353 		// Default settings
16354 		settings = self.settings = settings || {};
16355 
16356 		// Strange app protocol that isn't http/https or local anchor
16357 		// For example: mailto,skype,tel etc.
16358 		if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
16359 			self.source = url;
16360 			return;
16361 		}
16362 
16363 		var isProtocolRelative = url.indexOf('//') === 0;
16364 
16365 		// Absolute path with no host, fake host and protocol
16366 		if (url.indexOf('/') === 0 && !isProtocolRelative) {
16367 			url = (settings.base_uri ? settings.base_uri.protocol || 'http' : 'http') + '://mce_host' + url;
16368 		}
16369 
16370 		// Relative path http:// or protocol relative //path
16371 		if (!/^[\w\-]*:?\/\//.test(url)) {
16372 			base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory;
16373 			if (settings.base_uri.protocol === "") {
16374 				url = '//mce_host' + self.toAbsPath(base_url, url);
16375 			} else {
16376 				url = ((settings.base_uri && settings.base_uri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url);
16377 			}
16378 		}
16379 
16380 		// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
16381 		url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
16382 
16383 		/*jshint maxlen: 255 */
16384 		/*eslint max-len: 0 */
16385 		url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
16386 
16387 		each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
16388 			var part = url[i];
16389 
16390 			// Zope 3 workaround, they use @@something
16391 			if (part) {
16392 				part = part.replace(/\(mce_at\)/g, '@@');
16393 			}
16394 
16395 			self[v] = part;
16396 		});
16397 
16398 		baseUri = settings.base_uri;
16399 		if (baseUri) {
16400 			if (!self.protocol) {
16401 				self.protocol = baseUri.protocol;
16402 			}
16403 
16404 			if (!self.userInfo) {
16405 				self.userInfo = baseUri.userInfo;
16406 			}
16407 
16408 			if (!self.port && self.host === 'mce_host') {
16409 				self.port = baseUri.port;
16410 			}
16411 
16412 			if (!self.host || self.host === 'mce_host') {
16413 				self.host = baseUri.host;
16414 			}
16415 
16416 			self.source = '';
16417 		}
16418 
16419 		if (isProtocolRelative) {
16420 			self.protocol = '';
16421 		}
16422 
16423 		//t.path = t.path || '/';
16424 	}
16425 
16426 	URI.prototype = {
16427 		/**
16428 		 * Sets the internal path part of the URI.
16429 		 *
16430 		 * @method setPath
16431 		 * @param {string} path Path string to set.
16432 		 */
16433 		setPath: function(path) {
16434 			var self = this;
16435 
16436 			path = /^(.*?)\/?(\w+)?$/.exec(path);
16437 
16438 			// Update path parts
16439 			self.path = path[0];
16440 			self.directory = path[1];
16441 			self.file = path[2];
16442 
16443 			// Rebuild source
16444 			self.source = '';
16445 			self.getURI();
16446 		},
16447 
16448 		/**
16449 		 * Converts the specified URI into a relative URI based on the current URI instance location.
16450 		 *
16451 		 * @method toRelative
16452 		 * @param {String} uri URI to convert into a relative path/URI.
16453 		 * @return {String} Relative URI from the point specified in the current URI instance.
16454 		 * @example
16455 		 * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm
16456 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm');
16457 		 */
16458 		toRelative: function(uri) {
16459 			var self = this, output;
16460 
16461 			if (uri === "./") {
16462 				return uri;
16463 			}
16464 
16465 			uri = new URI(uri, {base_uri: self});
16466 
16467 			// Not on same domain/port or protocol
16468 			if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port ||
16469 				(self.protocol != uri.protocol && uri.protocol !== "")) {
16470 				return uri.getURI();
16471 			}
16472 
16473 			var tu = self.getURI(), uu = uri.getURI();
16474 
16475 			// Allow usage of the base_uri when relative_urls = true
16476 			if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
16477 				return tu;
16478 			}
16479 
16480 			output = self.toRelPath(self.path, uri.path);
16481 
16482 			// Add query
16483 			if (uri.query) {
16484 				output += '?' + uri.query;
16485 			}
16486 
16487 			// Add anchor
16488 			if (uri.anchor) {
16489 				output += '#' + uri.anchor;
16490 			}
16491 
16492 			return output;
16493 		},
16494 
16495 		/**
16496 		 * Converts the specified URI into a absolute URI based on the current URI instance location.
16497 		 *
16498 		 * @method toAbsolute
16499 		 * @param {String} uri URI to convert into a relative path/URI.
16500 		 * @param {Boolean} noHost No host and protocol prefix.
16501 		 * @return {String} Absolute URI from the point specified in the current URI instance.
16502 		 * @example
16503 		 * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm
16504 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm');
16505 		 */
16506 		toAbsolute: function(uri, noHost) {
16507 			uri = new URI(uri, {base_uri: this});
16508 
16509 			return uri.getURI(noHost && this.isSameOrigin(uri));
16510 		},
16511 
16512 		/**
16513 		 * Determine whether the given URI has the same origin as this URI.  Based on RFC-6454.
16514 		 * Supports default ports for protocols listed in DEFAULT_PORTS.  Unsupported protocols will fail safe: they
16515 		 * won't match, if the port specifications differ.
16516 		 *
16517 		 * @method isSameOrigin
16518 		 * @param {tinymce.util.URI} uri Uri instance to compare.
16519 		 * @returns {Boolean} True if the origins are the same.
16520 		 */
16521 		isSameOrigin: function(uri) {
16522 			if (this.host == uri.host && this.protocol == uri.protocol){
16523 				if (this.port == uri.port) {
16524 					return true;
16525 				}
16526 
16527 				var defaultPort = DEFAULT_PORTS[this.protocol];
16528 				if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) {
16529 					return true;
16530 				}
16531 			}
16532 
16533 			return false;
16534 		},
16535 
16536 		/**
16537 		 * Converts a absolute path into a relative path.
16538 		 *
16539 		 * @method toRelPath
16540 		 * @param {String} base Base point to convert the path from.
16541 		 * @param {String} path Absolute path to convert into a relative path.
16542 		 */
16543 		toRelPath: function(base, path) {
16544 			var items, breakPoint = 0, out = '', i, l;
16545 
16546 			// Split the paths
16547 			base = base.substring(0, base.lastIndexOf('/'));
16548 			base = base.split('/');
16549 			items = path.split('/');
16550 
16551 			if (base.length >= items.length) {
16552 				for (i = 0, l = base.length; i < l; i++) {
16553 					if (i >= items.length || base[i] != items[i]) {
16554 						breakPoint = i + 1;
16555 						break;
16556 					}
16557 				}
16558 			}
16559 
16560 			if (base.length < items.length) {
16561 				for (i = 0, l = items.length; i < l; i++) {
16562 					if (i >= base.length || base[i] != items[i]) {
16563 						breakPoint = i + 1;
16564 						break;
16565 					}
16566 				}
16567 			}
16568 
16569 			if (breakPoint === 1) {
16570 				return path;
16571 			}
16572 
16573 			for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) {
16574 				out += "../";
16575 			}
16576 
16577 			for (i = breakPoint - 1, l = items.length; i < l; i++) {
16578 				if (i != breakPoint - 1) {
16579 					out += "/" + items[i];
16580 				} else {
16581 					out += items[i];
16582 				}
16583 			}
16584 
16585 			return out;
16586 		},
16587 
16588 		/**
16589 		 * Converts a relative path into a absolute path.
16590 		 *
16591 		 * @method toAbsPath
16592 		 * @param {String} base Base point to convert the path from.
16593 		 * @param {String} path Relative path to convert into an absolute path.
16594 		 */
16595 		toAbsPath: function(base, path) {
16596 			var i, nb = 0, o = [], tr, outPath;
16597 
16598 			// Split paths
16599 			tr = /\/$/.test(path) ? '/' : '';
16600 			base = base.split('/');
16601 			path = path.split('/');
16602 
16603 			// Remove empty chunks
16604 			each(base, function(k) {
16605 				if (k) {
16606 					o.push(k);
16607 				}
16608 			});
16609 
16610 			base = o;
16611 
16612 			// Merge relURLParts chunks
16613 			for (i = path.length - 1, o = []; i >= 0; i--) {
16614 				// Ignore empty or .
16615 				if (path[i].length === 0 || path[i] === ".") {
16616 					continue;
16617 				}
16618 
16619 				// Is parent
16620 				if (path[i] === '..') {
16621 					nb++;
16622 					continue;
16623 				}
16624 
16625 				// Move up
16626 				if (nb > 0) {
16627 					nb--;
16628 					continue;
16629 				}
16630 
16631 				o.push(path[i]);
16632 			}
16633 
16634 			i = base.length - nb;
16635 
16636 			// If /a/b/c or /
16637 			if (i <= 0) {
16638 				outPath = o.reverse().join('/');
16639 			} else {
16640 				outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
16641 			}
16642 
16643 			// Add front / if it's needed
16644 			if (outPath.indexOf('/') !== 0) {
16645 				outPath = '/' + outPath;
16646 			}
16647 
16648 			// Add traling / if it's needed
16649 			if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) {
16650 				outPath += tr;
16651 			}
16652 
16653 			return outPath;
16654 		},
16655 
16656 		/**
16657 		 * Returns the full URI of the internal structure.
16658 		 *
16659 		 * @method getURI
16660 		 * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false.
16661 		 */
16662 		getURI: function(noProtoHost) {
16663 			var s, self = this;
16664 
16665 			// Rebuild source
16666 			if (!self.source || noProtoHost) {
16667 				s = '';
16668 
16669 				if (!noProtoHost) {
16670 					if (self.protocol) {
16671 						s += self.protocol + '://';
16672 					} else {
16673 						s += '//';
16674 					}
16675 
16676 					if (self.userInfo) {
16677 						s += self.userInfo + '@';
16678 					}
16679 
16680 					if (self.host) {
16681 						s += self.host;
16682 					}
16683 
16684 					if (self.port) {
16685 						s += ':' + self.port;
16686 					}
16687 				}
16688 
16689 				if (self.path) {
16690 					s += self.path;
16691 				}
16692 
16693 				if (self.query) {
16694 					s += '?' + self.query;
16695 				}
16696 
16697 				if (self.anchor) {
16698 					s += '#' + self.anchor;
16699 				}
16700 
16701 				self.source = s;
16702 			}
16703 
16704 			return self.source;
16705 		}
16706 	};
16707 
16708 	return URI;
16709 });
16710 
16711 // Included from: js/tinymce/classes/util/Class.js
16712 
16713 /**
16714  * Class.js
16715  *
16716  * Copyright 2003-2012, Moxiecode Systems AB, All rights reserved.
16717  */
16718 
16719 /**
16720  * This utilitiy class is used for easier inheritage.
16721  *
16722  * Features:
16723  * * Exposed super functions: this._super();
16724  * * Mixins
16725  * * Dummy functions
16726  * * Property functions: var value = object.value(); and object.value(newValue);
16727  * * Static functions
16728  * * Defaults settings
16729  */
16730 define("tinymce/util/Class", [
16731 	"tinymce/util/Tools"
16732 ], function(Tools) {
16733 	var each = Tools.each, extend = Tools.extend;
16734 
16735 	var extendClass, initializing;
16736 
16737 	function Class() {
16738 	}
16739 
16740 	// Provides classical inheritance, based on code made by John Resig
16741 	Class.extend = extendClass = function(prop) {
16742 		var self = this, _super = self.prototype, prototype, name, member;
16743 
16744 		// The dummy class constructor
16745 		function Class() {
16746 			var i, mixins, mixin, self = this;
16747 
16748 			// All construction is actually done in the init method
16749 			if (!initializing) {
16750 				// Run class constuctor
16751 				if (self.init) {
16752 					self.init.apply(self, arguments);
16753 				}
16754 
16755 				// Run mixin constructors
16756 				mixins = self.Mixins;
16757 				if (mixins) {
16758 					i = mixins.length;
16759 					while (i--) {
16760 						mixin = mixins[i];
16761 						if (mixin.init) {
16762 							mixin.init.apply(self, arguments);
16763 						}
16764 					}
16765 				}
16766 			}
16767 		}
16768 
16769 		// Dummy function, needs to be extended in order to provide functionality
16770 		function dummy() {
16771 			return this;
16772 		}
16773 
16774 		// Creates a overloaded method for the class
16775 		// this enables you to use this._super(); to call the super function
16776 		function createMethod(name, fn) {
16777 			return function(){
16778 				var self = this, tmp = self._super, ret;
16779 
16780 				self._super = _super[name];
16781 				ret = fn.apply(self, arguments);
16782 				self._super = tmp;
16783 
16784 				return ret;
16785 			};
16786 		}
16787 
16788 		// Instantiate a base class (but only create the instance,
16789 		// don't run the init constructor)
16790 		initializing = true;
16791 		
16792 		/*eslint new-cap:0 */
16793 		prototype = new self();
16794 		initializing = false;
16795 
16796 		// Add mixins
16797 		if (prop.Mixins) {
16798 			each(prop.Mixins, function(mixin) {
16799 				mixin = mixin;
16800 
16801 				for (var name in mixin) {
16802 					if (name !== "init") {
16803 						prop[name] = mixin[name];
16804 					}
16805 				}
16806 			});
16807 
16808 			if (_super.Mixins) {
16809 				prop.Mixins = _super.Mixins.concat(prop.Mixins);
16810 			}
16811 		}
16812 
16813 		// Generate dummy methods
16814 		if (prop.Methods) {
16815 			each(prop.Methods.split(','), function(name) {
16816 				prop[name] = dummy;
16817 			});
16818 		}
16819 
16820 		// Generate property methods
16821 		if (prop.Properties) {
16822 			each(prop.Properties.split(','), function(name) {
16823 				var fieldName = '_' + name;
16824 
16825 				prop[name] = function(value) {
16826 					var self = this, undef;
16827 
16828 					// Set value
16829 					if (value !== undef) {
16830 						self[fieldName] = value;
16831 
16832 						return self;
16833 					}
16834 
16835 					// Get value
16836 					return self[fieldName];
16837 				};
16838 			});
16839 		}
16840 
16841 		// Static functions
16842 		if (prop.Statics) {
16843 			each(prop.Statics, function(func, name) {
16844 				Class[name] = func;
16845 			});
16846 		}
16847 
16848 		// Default settings
16849 		if (prop.Defaults && _super.Defaults) {
16850 			prop.Defaults = extend({}, _super.Defaults, prop.Defaults);
16851 		}
16852 
16853 		// Copy the properties over onto the new prototype
16854 		for (name in prop) {
16855 			member = prop[name];
16856 
16857 			if (typeof member == "function" && _super[name]) {
16858 				prototype[name] = createMethod(name, member);
16859 			} else {
16860 				prototype[name] = member;
16861 			}
16862 		}
16863 
16864 		// Populate our constructed prototype object
16865 		Class.prototype = prototype;
16866 
16867 		// Enforce the constructor to be what we expect
16868 		Class.constructor = Class;
16869 
16870 		// And make this class extendible
16871 		Class.extend = extendClass;
16872 
16873 		return Class;
16874 	};
16875 
16876 	return Class;
16877 });
16878 
16879 // Included from: js/tinymce/classes/util/EventDispatcher.js
16880 
16881 /**
16882  * EventDispatcher.js
16883  *
16884  * Copyright, Moxiecode Systems AB
16885  * Released under LGPL License.
16886  *
16887  * License: http://www.tinymce.com/license
16888  * Contributing: http://www.tinymce.com/contributing
16889  */
16890 
16891 /**
16892  * This class lets you add/remove and fire events by name on the specified scope. This makes
16893  * it easy to add event listener logic to any class.
16894  *
16895  * @class tinymce.util.EventDispatcher
16896  * @example
16897  *  var eventDispatcher = new EventDispatcher();
16898  *
16899  *  eventDispatcher.on('click', function() {console.log('data');});
16900  *  eventDispatcher.fire('click', {data: 123});
16901  */
16902 define("tinymce/util/EventDispatcher", [
16903 	"tinymce/util/Tools"
16904 ], function(Tools) {
16905 	var nativeEvents = Tools.makeMap(
16906 		"focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
16907 		"mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
16908 		"draggesture dragdrop drop drag submit",
16909 		' '
16910 	);
16911 
16912 	function Dispatcher(settings) {
16913 		var self = this, scope, bindings = {}, toggleEvent;
16914 
16915 		function returnFalse() {
16916 			return false;
16917 		}
16918 
16919 		function returnTrue() {
16920 			return true;
16921 		}
16922 
16923 		settings = settings || {};
16924 		scope = settings.scope || self;
16925 		toggleEvent = settings.toggleEvent || returnFalse;
16926 
16927 		/**
16928 		 * Fires the specified event by name.
16929 		 *
16930 		 * @method fire
16931 		 * @param {String} name Name of the event to fire.
16932 		 * @param {Object?} args Event arguments.
16933 		 * @return {Object} Event args instance passed in.
16934 		 * @example
16935 		 * instance.fire('event', {...});
16936 		 */
16937 		function fire(name, args) {
16938 			var handlers, i, l, callback;
16939 
16940 			name = name.toLowerCase();
16941 			args = args || {};
16942 			args.type = name;
16943 
16944 			// Setup target is there isn't one
16945 			if (!args.target) {
16946 				args.target = scope;
16947 			}
16948 
16949 			// Add event delegation methods if they are missing
16950 			if (!args.preventDefault) {
16951 				// Add preventDefault method
16952 				args.preventDefault = function() {
16953 					args.isDefaultPrevented = returnTrue;
16954 				};
16955 
16956 				// Add stopPropagation
16957 				args.stopPropagation = function() {
16958 					args.isPropagationStopped = returnTrue;
16959 				};
16960 
16961 				// Add stopImmediatePropagation
16962 				args.stopImmediatePropagation = function() {
16963 					args.isImmediatePropagationStopped = returnTrue;
16964 				};
16965 
16966 				// Add event delegation states
16967 				args.isDefaultPrevented = returnFalse;
16968 				args.isPropagationStopped = returnFalse;
16969 				args.isImmediatePropagationStopped = returnFalse;
16970 			}
16971 
16972 			if (settings.beforeFire) {
16973 				settings.beforeFire(args);
16974 			}
16975 
16976 			handlers = bindings[name];
16977 			if (handlers) {
16978 				for (i = 0, l = handlers.length; i < l; i++) {
16979 					handlers[i] = callback = handlers[i];
16980 
16981 					// Stop immediate propagation if needed
16982 					if (args.isImmediatePropagationStopped()) {
16983 						args.stopPropagation();
16984 						return args;
16985 					}
16986 
16987 					// If callback returns false then prevent default and stop all propagation
16988 					if (callback.call(scope, args) === false) {
16989 						args.preventDefault();
16990 						return args;
16991 					}
16992 				}
16993 			}
16994 
16995 			return args;
16996 		}
16997 
16998 		/**
16999 		 * Binds an event listener to a specific event by name.
17000 		 *
17001 		 * @method on
17002 		 * @param {String} name Event name or space separated list of events to bind.
17003 		 * @param {callback} callback Callback to be executed when the event occurs.
17004 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
17005 		 * @return {Object} Current class instance.
17006 		 * @example
17007 		 * instance.on('event', function(e) {
17008 		 *     // Callback logic
17009 		 * });
17010 		 */
17011 		function on(name, callback, prepend) {
17012 			var handlers, names, i;
17013 
17014 			if (callback === false) {
17015 				callback = returnFalse;
17016 			}
17017 
17018 			if (callback) {
17019 				names = name.toLowerCase().split(' ');
17020 				i = names.length;
17021 				while (i--) {
17022 					name = names[i];
17023 					handlers = bindings[name];
17024 					if (!handlers) {
17025 						handlers = bindings[name] = [];
17026 						toggleEvent(name, true);
17027 					}
17028 
17029 					if (prepend) {
17030 						handlers.unshift(callback);
17031 					} else {
17032 						handlers.push(callback);
17033 					}
17034 				}
17035 			}
17036 
17037 			return self;
17038 		}
17039 
17040 		/**
17041 		 * Unbinds an event listener to a specific event by name.
17042 		 *
17043 		 * @method off
17044 		 * @param {String?} name Name of the event to unbind.
17045 		 * @param {callback?} callback Callback to unbind.
17046 		 * @return {Object} Current class instance.
17047 		 * @example
17048 		 * // Unbind specific callback
17049 		 * instance.off('event', handler);
17050 		 *
17051 		 * // Unbind all listeners by name
17052 		 * instance.off('event');
17053 		 *
17054 		 * // Unbind all events
17055 		 * instance.off();
17056 		 */
17057 		function off(name, callback) {
17058 			var i, handlers, bindingName, names, hi;
17059 
17060 			if (name) {
17061 				names = name.toLowerCase().split(' ');
17062 				i = names.length;
17063 				while (i--) {
17064 					name = names[i];
17065 					handlers = bindings[name];
17066 
17067 					// Unbind all handlers
17068 					if (!name) {
17069 						for (bindingName in bindings) {
17070 							toggleEvent(bindingName, false);
17071 							delete bindings[bindingName];
17072 						}
17073 
17074 						return self;
17075 					}
17076 
17077 					if (handlers) {
17078 						// Unbind all by name
17079 						if (!callback) {
17080 							handlers.length = 0;
17081 						} else {
17082 							// Unbind specific ones
17083 							hi = handlers.length;
17084 							while (hi--) {
17085 								if (handlers[hi] === callback) {
17086 									handlers.splice(hi, 1);
17087 								}
17088 							}
17089 						}
17090 
17091 						if (!handlers.length) {
17092 							toggleEvent(name, false);
17093 							delete bindings[name];
17094 						}
17095 					}
17096 				}
17097 			} else {
17098 				for (name in bindings) {
17099 					toggleEvent(name, false);
17100 				}
17101 
17102 				bindings = {};
17103 			}
17104 
17105 			return self;
17106 		}
17107 
17108 		/**
17109 		 * Returns true/false if the dispatcher has a event of the specified name.
17110 		 *
17111 		 * @method has
17112 		 * @param {String} name Name of the event to check for.
17113 		 * @return {Boolean} true/false if the event exists or not.
17114 		 */
17115 		function has(name) {
17116 			name = name.toLowerCase();
17117 			return !(!bindings[name] || bindings[name].length === 0);
17118 		}
17119 
17120 		// Expose
17121 		self.fire = fire;
17122 		self.on = on;
17123 		self.off = off;
17124 		self.has = has;
17125 	}
17126 
17127 	/**
17128 	 * Returns true/false if the specified event name is a native browser event or not.
17129 	 *
17130 	 * @method isNative
17131 	 * @param {String} name Name to check if it's native.
17132 	 * @return {Boolean} true/false if the event is native or not.
17133 	 * @static
17134 	 */
17135 	Dispatcher.isNative = function(name) {
17136 		return !!nativeEvents[name.toLowerCase()];
17137 	};
17138 
17139 	return Dispatcher;
17140 });
17141 
17142 // Included from: js/tinymce/classes/ui/Selector.js
17143 
17144 /**
17145  * Selector.js
17146  *
17147  * Copyright, Moxiecode Systems AB
17148  * Released under LGPL License.
17149  *
17150  * License: http://www.tinymce.com/license
17151  * Contributing: http://www.tinymce.com/contributing
17152  */
17153 
17154 /*eslint no-nested-ternary:0 */
17155 
17156 /**
17157  * Selector engine, enables you to select controls by using CSS like expressions.
17158  * We currently only support basic CSS expressions to reduce the size of the core
17159  * and the ones we support should be enough for most cases.
17160  *
17161  * @example
17162  * Supported expressions:
17163  *  element
17164  *  element#name
17165  *  element.class
17166  *  element[attr]
17167  *  element[attr*=value]
17168  *  element[attr~=value]
17169  *  element[attr!=value]
17170  *  element[attr^=value]
17171  *  element[attr$=value]
17172  *  element:<state>
17173  *  element:not(<expression>)
17174  *  element:first
17175  *  element:last
17176  *  element:odd
17177  *  element:even
17178  *  element element
17179  *  element > element
17180  *
17181  * @class tinymce.ui.Selector
17182  */
17183 define("tinymce/ui/Selector", [
17184 	"tinymce/util/Class"
17185 ], function(Class) {
17186 	"use strict";
17187 
17188 	/**
17189 	 * Produces an array with a unique set of objects. It will not compare the values
17190 	 * but the references of the objects.
17191 	 *
17192 	 * @private
17193 	 * @method unqiue
17194 	 * @param {Array} array Array to make into an array with unique items.
17195 	 * @return {Array} Array with unique items.
17196 	 */
17197 	function unique(array) {
17198 		var uniqueItems = [], i = array.length, item;
17199 
17200 		while (i--) {
17201 			item = array[i];
17202 
17203 			if (!item.__checked) {
17204 				uniqueItems.push(item);
17205 				item.__checked = 1;
17206 			}
17207 		}
17208 
17209 		i = uniqueItems.length;
17210 		while (i--) {
17211 			delete uniqueItems[i].__checked;
17212 		}
17213 
17214 		return uniqueItems;
17215 	}
17216 
17217 	var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i;
17218 
17219 	/*jshint maxlen:255 */
17220 	/*eslint max-len:0 */
17221 	var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
17222 		whiteSpace = /^\s*|\s*$/g,
17223 		Collection;
17224 
17225 	var Selector = Class.extend({
17226 		/**
17227 		 * Constructs a new Selector instance.
17228 		 *
17229 		 * @constructor
17230 		 * @method init
17231 		 * @param {String} selector CSS like selector expression.
17232 		 */
17233 		init: function(selector) {
17234 			var match = this.match;
17235 
17236 			function compileNameFilter(name) {
17237 				if (name) {
17238 					name = name.toLowerCase();
17239 
17240 					return function(item) {
17241 						return name === '*' || item.type === name;
17242 					};
17243 				}
17244 			}
17245 
17246 			function compileIdFilter(id) {
17247 				if (id) {
17248 					return function(item) {
17249 						return item._name === id;
17250 					};
17251 				}
17252 			}
17253 
17254 			function compileClassesFilter(classes) {
17255 				if (classes) {
17256 					classes = classes.split('.');
17257 
17258 					return function(item) {
17259 						var i = classes.length;
17260 
17261 						while (i--) {
17262 							if (!item.hasClass(classes[i])) {
17263 								return false;
17264 							}
17265 						}
17266 
17267 						return true;
17268 					};
17269 				}
17270 			}
17271 
17272 			function compileAttrFilter(name, cmp, check) {
17273 				if (name) {
17274 					return function(item) {
17275 						var value = item[name] ? item[name]() : '';
17276 
17277 						return !cmp ? !!check :
17278 							cmp === "=" ? value === check :
17279 							cmp === "*=" ? value.indexOf(check) >= 0 :
17280 							cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 :
17281 							cmp === "!=" ? value != check :
17282 							cmp === "^=" ? value.indexOf(check) === 0 :
17283 							cmp === "$=" ? value.substr(value.length - check.length) === check :
17284 							false;
17285 					};
17286 				}
17287 			}
17288 
17289 			function compilePsuedoFilter(name) {
17290 				var notSelectors;
17291 
17292 				if (name) {
17293 					name = /(?:not\((.+)\))|(.+)/i.exec(name);
17294 
17295 					if (!name[1]) {
17296 						name = name[2];
17297 
17298 						return function(item, index, length) {
17299 							return name === 'first' ? index === 0 :
17300 								name === 'last' ? index === length - 1 :
17301 								name === 'even' ? index % 2 === 0 :
17302 								name === 'odd' ? index % 2 === 1 :
17303 								item[name] ? item[name]() :
17304 								false;
17305 						};
17306 					} else {
17307 						// Compile not expression
17308 						notSelectors = parseChunks(name[1], []);
17309 
17310 						return function(item) {
17311 							return !match(item, notSelectors);
17312 						};
17313 					}
17314 				}
17315 			}
17316 
17317 			function compile(selector, filters, direct) {
17318 				var parts;
17319 
17320 				function add(filter) {
17321 					if (filter) {
17322 						filters.push(filter);
17323 					}
17324 				}
17325 
17326 				// Parse expression into parts
17327 				parts = expression.exec(selector.replace(whiteSpace, ''));
17328 
17329 				add(compileNameFilter(parts[1]));
17330 				add(compileIdFilter(parts[2]));
17331 				add(compileClassesFilter(parts[3]));
17332 				add(compileAttrFilter(parts[4], parts[5], parts[6]));
17333 				add(compilePsuedoFilter(parts[7]));
17334 
17335 				// Mark the filter with psuedo for performance
17336 				filters.psuedo = !!parts[7];
17337 				filters.direct = direct;
17338 
17339 				return filters;
17340 			}
17341 
17342 			// Parser logic based on Sizzle by John Resig
17343 			function parseChunks(selector, selectors) {
17344 				var parts = [], extra, matches, i;
17345 
17346 				do {
17347 					chunker.exec("");
17348 					matches = chunker.exec(selector);
17349 
17350 					if (matches) {
17351 						selector = matches[3];
17352 						parts.push(matches[1]);
17353 
17354 						if (matches[2]) {
17355 							extra = matches[3];
17356 							break;
17357 						}
17358 					}
17359 				} while (matches);
17360 
17361 				if (extra) {
17362 					parseChunks(extra, selectors);
17363 				}
17364 
17365 				selector = [];
17366 				for (i = 0; i < parts.length; i++) {
17367 					if (parts[i] != '>') {
17368 						selector.push(compile(parts[i], [], parts[i - 1] === '>'));
17369 					}
17370 				}
17371 
17372 				selectors.push(selector);
17373 
17374 				return selectors;
17375 			}
17376 
17377 			this._selectors = parseChunks(selector, []);
17378 		},
17379 
17380 		/**
17381 		 * Returns true/false if the selector matches the specified control.
17382 		 *
17383 		 * @method match
17384 		 * @param {tinymce.ui.Control} control Control to match agains the selector.
17385 		 * @param {Array} selectors Optional array of selectors, mostly used internally.
17386 		 * @return {Boolean} true/false state if the control matches or not.
17387 		 */
17388 		match: function(control, selectors) {
17389 			var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item;
17390 
17391 			selectors = selectors || this._selectors;
17392 			for (i = 0, l = selectors.length; i < l; i++) {
17393 				selector = selectors[i];
17394 				sl = selector.length;
17395 				item = control;
17396 				count = 0;
17397 
17398 				for (si = sl - 1; si >= 0; si--) {
17399 					filters = selector[si];
17400 
17401 					while (item) {
17402 						// Find the index and length since a psuedo filter like :first needs it
17403 						if (filters.psuedo) {
17404 							siblings = item.parent().items();
17405 							index = length = siblings.length;
17406 							while (index--) {
17407 								if (siblings[index] === item) {
17408 									break;
17409 								}
17410 							}
17411 						}
17412 
17413 						for (fi = 0, fl = filters.length; fi < fl; fi++) {
17414 							if (!filters[fi](item, index, length)) {
17415 								fi = fl + 1;
17416 								break;
17417 							}
17418 						}
17419 
17420 						if (fi === fl) {
17421 							count++;
17422 							break;
17423 						} else {
17424 							// If it didn't match the right most expression then
17425 							// break since it's no point looking at the parents
17426 							if (si === sl - 1) {
17427 								break;
17428 							}
17429 						}
17430 
17431 						item = item.parent();
17432 					}
17433 				}
17434 
17435 				// If we found all selectors then return true otherwise continue looking
17436 				if (count === sl) {
17437 					return true;
17438 				}
17439 			}
17440 
17441 			return false;
17442 		},
17443 
17444 		/**
17445 		 * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container.
17446 		 *
17447 		 * @method find
17448 		 * @param {tinymce.ui.Control} container Container to look for items in.
17449 		 * @return {tinymce.ui.Collection} Collection with matched elements.
17450 		 */
17451 		find: function(container) {
17452 			var matches = [], i, l, selectors = this._selectors;
17453 
17454 			function collect(items, selector, index) {
17455 				var i, l, fi, fl, item, filters = selector[index];
17456 
17457 				for (i = 0, l = items.length; i < l; i++) {
17458 					item = items[i];
17459 
17460 					// Run each filter agains the item
17461 					for (fi = 0, fl = filters.length; fi < fl; fi++) {
17462 						if (!filters[fi](item, i, l)) {
17463 							fi = fl + 1;
17464 							break;
17465 						}
17466 					}
17467 
17468 					// All filters matched the item
17469 					if (fi === fl) {
17470 						// Matched item is on the last expression like: panel toolbar [button]
17471 						if (index == selector.length - 1) {
17472 							matches.push(item);
17473 						} else {
17474 							// Collect next expression type
17475 							if (item.items) {
17476 								collect(item.items(), selector, index + 1);
17477 							}
17478 						}
17479 					} else if (filters.direct) {
17480 						return;
17481 					}
17482 
17483 					// Collect child items
17484 					if (item.items) {
17485 						collect(item.items(), selector, index);
17486 					}
17487 				}
17488 			}
17489 
17490 			if (container.items) {
17491 				for (i = 0, l = selectors.length; i < l; i++) {
17492 					collect(container.items(), selectors[i], 0);
17493 				}
17494 
17495 				// Unique the matches if needed
17496 				if (l > 1) {
17497 					matches = unique(matches);
17498 				}
17499 			}
17500 
17501 			// Fix for circular reference
17502 			if (!Collection) {
17503 				// TODO: Fix me!
17504 				Collection = Selector.Collection;
17505 			}
17506 
17507 			return new Collection(matches);
17508 		}
17509 	});
17510 
17511 	return Selector;
17512 });
17513 
17514 // Included from: js/tinymce/classes/ui/Collection.js
17515 
17516 /**
17517  * Collection.js
17518  *
17519  * Copyright, Moxiecode Systems AB
17520  * Released under LGPL License.
17521  *
17522  * License: http://www.tinymce.com/license
17523  * Contributing: http://www.tinymce.com/contributing
17524  */
17525 
17526 /**
17527  * Control collection, this class contains control instances and it enables you to
17528  * perform actions on all the contained items. This is very similar to how jQuery works.
17529  *
17530  * @example
17531  * someCollection.show().disabled(true);
17532  *
17533  * @class tinymce.ui.Collection
17534  */
17535 define("tinymce/ui/Collection", [
17536 	"tinymce/util/Tools",
17537 	"tinymce/ui/Selector",
17538 	"tinymce/util/Class"
17539 ], function(Tools, Selector, Class) {
17540 	"use strict";
17541 
17542 	var Collection, proto, push = Array.prototype.push, slice = Array.prototype.slice;
17543 
17544 	proto = {
17545 		/**
17546 		 * Current number of contained control instances.
17547 		 *
17548 		 * @field length
17549 		 * @type Number
17550 		 */
17551 		length: 0,
17552 
17553 		/**
17554 		 * Constructor for the collection.
17555 		 *
17556 		 * @constructor
17557 		 * @method init
17558 		 * @param {Array} items Optional array with items to add.
17559 		 */
17560 		init: function(items) {
17561 			if (items) {
17562 				this.add(items);
17563 			}
17564 		},
17565 
17566 		/**
17567 		 * Adds new items to the control collection.
17568 		 *
17569 		 * @method add
17570 		 * @param {Array} items Array if items to add to collection.
17571 		 * @return {tinymce.ui.Collection} Current collection instance.
17572 		 */
17573 		add: function(items) {
17574 			var self = this;
17575 
17576 			// Force single item into array
17577 			if (!Tools.isArray(items)) {
17578 				if (items instanceof Collection) {
17579 					self.add(items.toArray());
17580 				} else {
17581 					push.call(self, items);
17582 				}
17583 			} else {
17584 				push.apply(self, items);
17585 			}
17586 
17587 			return self;
17588 		},
17589 
17590 		/**
17591 		 * Sets the contents of the collection. This will remove any existing items
17592 		 * and replace them with the ones specified in the input array.
17593 		 *
17594 		 * @method set
17595 		 * @param {Array} items Array with items to set into the Collection.
17596 		 * @return {tinymce.ui.Collection} Collection instance.
17597 		 */
17598 		set: function(items) {
17599 			var self = this, len = self.length, i;
17600 
17601 			self.length = 0;
17602 			self.add(items);
17603 
17604 			// Remove old entries
17605 			for (i = self.length; i < len; i++) {
17606 				delete self[i];
17607 			}
17608 
17609 			return self;
17610 		},
17611 
17612 		/**
17613 		 * Filters the collection item based on the specified selector expression or selector function.
17614 		 *
17615 		 * @method filter
17616 		 * @param {String} selector Selector expression to filter items by.
17617 		 * @return {tinymce.ui.Collection} Collection containing the filtered items.
17618 		 */
17619 		filter: function(selector) {
17620 			var self = this, i, l, matches = [], item, match;
17621 
17622 			// Compile string into selector expression
17623 			if (typeof(selector) === "string") {
17624 				selector = new Selector(selector);
17625 
17626 				match = function(item) {
17627 					return selector.match(item);
17628 				};
17629 			} else {
17630 				// Use selector as matching function
17631 				match = selector;
17632 			}
17633 
17634 			for (i = 0, l = self.length; i < l; i++) {
17635 				item = self[i];
17636 
17637 				if (match(item)) {
17638 					matches.push(item);
17639 				}
17640 			}
17641 
17642 			return new Collection(matches);
17643 		},
17644 
17645 		/**
17646 		 * Slices the items within the collection.
17647 		 *
17648 		 * @method slice
17649 		 * @param {Number} index Index to slice at.
17650 		 * @param {Number} len Optional length to slice.
17651 		 * @return {tinymce.ui.Collection} Current collection.
17652 		 */
17653 		slice: function() {
17654 			return new Collection(slice.apply(this, arguments));
17655 		},
17656 
17657 		/**
17658 		 * Makes the current collection equal to the specified index.
17659 		 *
17660 		 * @method eq
17661 		 * @param {Number} index Index of the item to set the collection to.
17662 		 * @return {tinymce.ui.Collection} Current collection.
17663 		 */
17664 		eq: function(index) {
17665 			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
17666 		},
17667 
17668 		/**
17669 		 * Executes the specified callback on each item in collection.
17670 		 *
17671 		 * @method each
17672 		 * @param {function} callback Callback to execute for each item in collection.
17673 		 * @return {tinymce.ui.Collection} Current collection instance.
17674 		 */
17675 		each: function(callback) {
17676 			Tools.each(this, callback);
17677 
17678 			return this;
17679 		},
17680 
17681 		/**
17682 		 * Returns an JavaScript array object of the contents inside the collection.
17683 		 *
17684 		 * @method toArray
17685 		 * @return {Array} Array with all items from collection.
17686 		 */
17687 		toArray: function() {
17688 			return Tools.toArray(this);
17689 		},
17690 
17691 		/**
17692 		 * Finds the index of the specified control or return -1 if it isn't in the collection.
17693 		 *
17694 		 * @method indexOf
17695 		 * @param {Control} ctrl Control instance to look for.
17696 		 * @return {Number} Index of the specified control or -1.
17697 		 */
17698 		indexOf: function(ctrl) {
17699 			var self = this, i = self.length;
17700 
17701 			while (i--) {
17702 				if (self[i] === ctrl) {
17703 					break;
17704 				}
17705 			}
17706 
17707 			return i;
17708 		},
17709 
17710 		/**
17711 		 * Returns a new collection of the contents in reverse order.
17712 		 *
17713 		 * @method reverse
17714 		 * @return {tinymce.ui.Collection} Collection instance with reversed items.
17715 		 */
17716 		reverse: function() {
17717 			return new Collection(Tools.toArray(this).reverse());
17718 		},
17719 
17720 		/**
17721 		 * Returns true/false if the class exists or not.
17722 		 *
17723 		 * @method hasClass
17724 		 * @param {String} cls Class to check for.
17725 		 * @return {Boolean} true/false state if the class exists or not.
17726 		 */
17727 		hasClass: function(cls) {
17728 			return this[0] ? this[0].hasClass(cls) : false;
17729 		},
17730 
17731 		/**
17732 		 * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>);
17733 		 *
17734 		 * @method prop
17735 		 * @param {String} name Property name to get/set.
17736 		 * @param {Object} value Optional object value to set.
17737 		 * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation.
17738 		 */
17739 		prop: function(name, value) {
17740 			var self = this, undef, item;
17741 
17742 			if (value !== undef) {
17743 				self.each(function(item) {
17744 					if (item[name]) {
17745 						item[name](value);
17746 					}
17747 				});
17748 
17749 				return self;
17750 			}
17751 
17752 			item = self[0];
17753 
17754 			if (item && item[name]) {
17755 				return item[name]();
17756 			}
17757 		},
17758 
17759 		/**
17760 		 * Executes the specific function name with optional arguments an all items in collection if it exists.
17761 		 *
17762 		 * @example collection.exec("myMethod", arg1, arg2, arg3);
17763 		 * @method exec
17764 		 * @param {String} name Name of the function to execute.
17765 		 * @param {Object} ... Multiple arguments to pass to each function.
17766 		 * @return {tinymce.ui.Collection} Current collection.
17767 		 */
17768 		exec: function(name) {
17769 			var self = this, args = Tools.toArray(arguments).slice(1);
17770 
17771 			self.each(function(item) {
17772 				if (item[name]) {
17773 					item[name].apply(item, args);
17774 				}
17775 			});
17776 
17777 			return self;
17778 		},
17779 
17780 		/**
17781 		 * Remove all items from collection and DOM.
17782 		 *
17783 		 * @method remove
17784 		 * @return {tinymce.ui.Collection} Current collection.
17785 		 */
17786 		remove: function() {
17787 			var i = this.length;
17788 
17789 			while (i--) {
17790 				this[i].remove();
17791 			}
17792 
17793 			return this;
17794 		}
17795 
17796 		/**
17797 		 * Fires the specified event by name and arguments on the control. This will execute all
17798 		 * bound event handlers.
17799 		 *
17800 		 * @method fire
17801 		 * @param {String} name Name of the event to fire.
17802 		 * @param {Object} args Optional arguments to pass to the event.
17803 		 * @return {tinymce.ui.Collection} Current collection instance.
17804 		 */
17805 		// fire: function(event, args) {}, -- Generated by code below
17806 
17807 		/**
17808 		 * Binds a callback to the specified event. This event can both be
17809 		 * native browser events like "click" or custom ones like PostRender.
17810 		 *
17811 		 * The callback function will have two parameters the first one being the control that received the event
17812 		 * the second one will be the event object either the browsers native event object or a custom JS object.
17813 		 *
17814 		 * @method on
17815 		 * @param {String} name Name of the event to bind. For example "click".
17816 		 * @param {String/function} callback Callback function to execute ones the event occurs.
17817 		 * @return {tinymce.ui.Collection} Current collection instance.
17818 		 */
17819 		// on: function(name, callback) {}, -- Generated by code below
17820 
17821 		/**
17822 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
17823 		 * parameter all event handlers will be removed. If you omit the callback all event handles
17824 		 * by the specified name will be removed.
17825 		 *
17826 		 * @method off
17827 		 * @param {String} name Optional name for the event to unbind.
17828 		 * @param {function} callback Optional callback function to unbind.
17829 		 * @return {tinymce.ui.Collection} Current collection instance.
17830 		 */
17831 		// off: function(name, callback) {}, -- Generated by code below
17832 
17833 		/**
17834 		 * Shows the items in the current collection.
17835 		 *
17836 		 * @method show
17837 		 * @return {tinymce.ui.Collection} Current collection instance.
17838 		 */
17839 		// show: function() {}, -- Generated by code below
17840 
17841 		/**
17842 		 * Hides the items in the current collection.
17843 		 *
17844 		 * @method hide
17845 		 * @return {tinymce.ui.Collection} Current collection instance.
17846 		 */
17847 		// hide: function() {}, -- Generated by code below
17848 
17849 		/**
17850 		 * Sets/gets the text contents of the items in the current collection.
17851 		 *
17852 		 * @method text
17853 		 * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation.
17854 		 */
17855 		// text: function(value) {}, -- Generated by code below
17856 
17857 		/**
17858 		 * Sets/gets the name contents of the items in the current collection.
17859 		 *
17860 		 * @method name
17861 		 * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation.
17862 		 */
17863 		// name: function(value) {}, -- Generated by code below
17864 
17865 		/**
17866 		 * Sets/gets the disabled state on the items in the current collection.
17867 		 *
17868 		 * @method disabled
17869 		 * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation.
17870 		 */
17871 		// disabled: function(state) {}, -- Generated by code below
17872 
17873 		/**
17874 		 * Sets/gets the active state on the items in the current collection.
17875 		 *
17876 		 * @method active
17877 		 * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation.
17878 		 */
17879 		// active: function(state) {}, -- Generated by code below
17880 
17881 		/**
17882 		 * Sets/gets the selected state on the items in the current collection.
17883 		 *
17884 		 * @method selected
17885 		 * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation.
17886 		 */
17887 		// selected: function(state) {}, -- Generated by code below
17888 
17889 		/**
17890 		 * Sets/gets the selected state on the items in the current collection.
17891 		 *
17892 		 * @method visible
17893 		 * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation.
17894 		 */
17895 		// visible: function(state) {}, -- Generated by code below
17896 
17897 		/**
17898 		 * Adds a class to all items in the collection.
17899 		 *
17900 		 * @method addClass
17901 		 * @param {String} cls Class to add to each item.
17902 		 * @return {tinymce.ui.Collection} Current collection instance.
17903 		 */
17904 		// addClass: function(cls) {}, -- Generated by code below
17905 
17906 		/**
17907 		 * Removes the specified class from all items in collection.
17908 		 *
17909 		 * @method removeClass
17910 		 * @param {String} cls Class to remove from each item.
17911 		 * @return {tinymce.ui.Collection} Current collection instance.
17912 		 */
17913 		// removeClass: function(cls) {}, -- Generated by code below
17914 	};
17915 
17916 	// Extend tinymce.ui.Collection prototype with some generated control specific methods
17917 	Tools.each('fire on off show hide addClass removeClass append prepend before after reflow'.split(' '), function(name) {
17918 		proto[name] = function() {
17919 			var args = Tools.toArray(arguments);
17920 
17921 			this.each(function(ctrl) {
17922 				if (name in ctrl) {
17923 					ctrl[name].apply(ctrl, args);
17924 				}
17925 			});
17926 
17927 			return this;
17928 		};
17929 	});
17930 
17931 	// Extend tinymce.ui.Collection prototype with some property methods
17932 	Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) {
17933 		proto[name] = function(value) {
17934 			return this.prop(name, value);
17935 		};
17936 	});
17937 
17938 	// Create class based on the new prototype
17939 	Collection = Class.extend(proto);
17940 
17941 	// Stick Collection into Selector to prevent circual references
17942 	Selector.Collection = Collection;
17943 
17944 	return Collection;
17945 });
17946 
17947 // Included from: js/tinymce/classes/ui/DomUtils.js
17948 
17949 /**
17950  * DOMUtils.js
17951  *
17952  * Copyright, Moxiecode Systems AB
17953  * Released under LGPL License.
17954  *
17955  * License: http://www.tinymce.com/license
17956  * Contributing: http://www.tinymce.com/contributing
17957  */
17958 
17959 define("tinymce/ui/DomUtils", [
17960 	"tinymce/util/Tools",
17961 	"tinymce/dom/DOMUtils"
17962 ], function(Tools, DOMUtils) {
17963 	"use strict";
17964 
17965 	return {
17966 		id: function() {
17967 			return DOMUtils.DOM.uniqueId();
17968 		},
17969 
17970 		createFragment: function(html) {
17971 			return DOMUtils.DOM.createFragment(html);
17972 		},
17973 
17974 		getWindowSize: function() {
17975 			return DOMUtils.DOM.getViewPort();
17976 		},
17977 
17978 		getSize: function(elm) {
17979 			var width, height;
17980 
17981 			if (elm.getBoundingClientRect) {
17982 				var rect = elm.getBoundingClientRect();
17983 
17984 				width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth);
17985 				height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight);
17986 			} else {
17987 				width = elm.offsetWidth;
17988 				height = elm.offsetHeight;
17989 			}
17990 
17991 			return {width: width, height: height};
17992 		},
17993 
17994 		getPos: function(elm, root) {
17995 			return DOMUtils.DOM.getPos(elm, root);
17996 		},
17997 
17998 		getViewPort: function(win) {
17999 			return DOMUtils.DOM.getViewPort(win);
18000 		},
18001 
18002 		get: function(id) {
18003 			return document.getElementById(id);
18004 		},
18005 
18006 		addClass : function(elm, cls) {
18007 			return DOMUtils.DOM.addClass(elm, cls);
18008 		},
18009 
18010 		removeClass : function(elm, cls) {
18011 			return DOMUtils.DOM.removeClass(elm, cls);
18012 		},
18013 
18014 		hasClass : function(elm, cls) {
18015 			return DOMUtils.DOM.hasClass(elm, cls);
18016 		},
18017 
18018 		toggleClass: function(elm, cls, state) {
18019 			return DOMUtils.DOM.toggleClass(elm, cls, state);
18020 		},
18021 
18022 		css: function(elm, name, value) {
18023 			return DOMUtils.DOM.setStyle(elm, name, value);
18024 		},
18025 
18026 		on: function(target, name, callback, scope) {
18027 			return DOMUtils.DOM.bind(target, name, callback, scope);
18028 		},
18029 
18030 		off: function(target, name, callback) {
18031 			return DOMUtils.DOM.unbind(target, name, callback);
18032 		},
18033 
18034 		fire: function(target, name, args) {
18035 			return DOMUtils.DOM.fire(target, name, args);
18036 		},
18037 
18038 		innerHtml: function(elm, html) {
18039 			// Workaround for <div> in <p> bug on IE 8 #6178
18040 			DOMUtils.DOM.setHTML(elm, html);
18041 		}
18042 	};
18043 });
18044 
18045 // Included from: js/tinymce/classes/ui/Control.js
18046 
18047 /**
18048  * Control.js
18049  *
18050  * Copyright, Moxiecode Systems AB
18051  * Released under LGPL License.
18052  *
18053  * License: http://www.tinymce.com/license
18054  * Contributing: http://www.tinymce.com/contributing
18055  */
18056 
18057 /*eslint consistent-this:0 */
18058 
18059 /**
18060  * This is the base class for all controls and containers. All UI control instances inherit
18061  * from this one as it has the base logic needed by all of them.
18062  *
18063  * @class tinymce.ui.Control
18064  */
18065 define("tinymce/ui/Control", [
18066 	"tinymce/util/Class",
18067 	"tinymce/util/Tools",
18068 	"tinymce/util/EventDispatcher",
18069 	"tinymce/ui/Collection",
18070 	"tinymce/ui/DomUtils"
18071 ], function(Class, Tools, EventDispatcher, Collection, DomUtils) {
18072 	"use strict";
18073 
18074 	var elementIdCache = {};
18075 	var hasMouseWheelEventSupport = "onmousewheel" in document;
18076 	var hasWheelEventSupport = false;
18077 	var classPrefix = "mce-";
18078 
18079 	function getEventDispatcher(obj) {
18080 		if (!obj._eventDispatcher) {
18081 			obj._eventDispatcher = new EventDispatcher({
18082 				scope: obj,
18083 				toggleEvent: function(name, state) {
18084 					if (state && EventDispatcher.isNative(name)) {
18085 						if (!obj._nativeEvents) {
18086 							obj._nativeEvents = {};
18087 						}
18088 
18089 						obj._nativeEvents[name] = true;
18090 
18091 						if (obj._rendered) {
18092 							obj.bindPendingEvents();
18093 						}
18094 					}
18095 				}
18096 			});
18097 		}
18098 
18099 		return obj._eventDispatcher;
18100 	}
18101 
18102 	var Control = Class.extend({
18103 		Statics: {
18104 			elementIdCache: elementIdCache,
18105 			classPrefix: classPrefix
18106 		},
18107 
18108 		isRtl: function() {
18109 			return Control.rtl;
18110 		},
18111 
18112 		/**
18113 		 * Class/id prefix to use for all controls.
18114 		 *
18115 		 * @final
18116 		 * @field {String} classPrefix
18117 		 */
18118 		classPrefix: classPrefix,
18119 
18120 		/**
18121 		 * Constructs a new control instance with the specified settings.
18122 		 *
18123 		 * @constructor
18124 		 * @param {Object} settings Name/value object with settings.
18125 		 * @setting {String} style Style CSS properties to add.
18126 		 * @setting {String} border Border box values example: 1 1 1 1
18127 		 * @setting {String} padding Padding box values example: 1 1 1 1
18128 		 * @setting {String} margin Margin box values example: 1 1 1 1
18129 		 * @setting {Number} minWidth Minimal width for the control.
18130 		 * @setting {Number} minHeight Minimal height for the control.
18131 		 * @setting {String} classes Space separated list of classes to add.
18132 		 * @setting {String} role WAI-ARIA role to use for control.
18133 		 * @setting {Boolean} hidden Is the control hidden by default.
18134 		 * @setting {Boolean} disabled Is the control disabled by default.
18135 		 * @setting {String} name Name of the control instance.
18136 		 */
18137 		init: function(settings) {
18138 			var self = this, classes, i;
18139 
18140 			self.settings = settings = Tools.extend({}, self.Defaults, settings);
18141 
18142 			// Initial states
18143 			self._id = settings.id || DomUtils.id();
18144 			self._text = self._name = '';
18145 			self._width = self._height = 0;
18146 			self._aria = {role: settings.role};
18147 
18148 			// Setup classes
18149 			classes = settings.classes;
18150 			if (classes) {
18151 				classes = classes.split(' ');
18152 				classes.map = {};
18153 				i = classes.length;
18154 				while (i--) {
18155 					classes.map[classes[i]] = true;
18156 				}
18157 			}
18158 
18159 			self._classes = classes || [];
18160 			self.visible(true);
18161 
18162 			// Set some properties
18163 			Tools.each('title text width height name classes visible disabled active value'.split(' '), function(name) {
18164 				var value = settings[name], undef;
18165 
18166 				if (value !== undef) {
18167 					self[name](value);
18168 				} else if (self['_' + name] === undef) {
18169 					self['_' + name] = false;
18170 				}
18171 			});
18172 
18173 			self.on('click', function() {
18174 				if (self.disabled()) {
18175 					return false;
18176 				}
18177 			});
18178 
18179 			// TODO: Is this needed duplicate code see above?
18180 			if (settings.classes) {
18181 				Tools.each(settings.classes.split(' '), function(cls) {
18182 					self.addClass(cls);
18183 				});
18184 			}
18185 
18186 			/**
18187 			 * Name/value object with settings for the current control.
18188 			 *
18189 			 * @field {Object} settings
18190 			 */
18191 			self.settings = settings;
18192 
18193 			self._borderBox = self.parseBox(settings.border);
18194 			self._paddingBox = self.parseBox(settings.padding);
18195 			self._marginBox = self.parseBox(settings.margin);
18196 
18197 			if (settings.hidden) {
18198 				self.hide();
18199 			}
18200 		},
18201 
18202 		// Will generate getter/setter methods for these properties
18203 		Properties: 'parent,title,text,width,height,disabled,active,name,value',
18204 
18205 		// Will generate empty dummy functions for these
18206 		Methods: 'renderHtml',
18207 
18208 		/**
18209 		 * Returns the root element to render controls into.
18210 		 *
18211 		 * @method getContainerElm
18212 		 * @return {Element} HTML DOM element to render into.
18213 		 */
18214 		getContainerElm: function() {
18215 			return document.body;
18216 		},
18217 
18218 		/**
18219 		 * Returns a control instance for the current DOM element.
18220 		 *
18221 		 * @method getParentCtrl
18222 		 * @param {Element} elm HTML dom element to get parent control from.
18223 		 * @return {tinymce.ui.Control} Control instance or undefined.
18224 		 */
18225 		getParentCtrl: function(elm) {
18226 			var ctrl, lookup = this.getRoot().controlIdLookup;
18227 
18228 			while (elm && lookup) {
18229 				ctrl = lookup[elm.id];
18230 				if (ctrl) {
18231 					break;
18232 				}
18233 
18234 				elm = elm.parentNode;
18235 			}
18236 
18237 			return ctrl;
18238 		},
18239 
18240 		/**
18241 		 * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
18242 		 *
18243 		 * @method parseBox
18244 		 * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
18245 		 * @return {Object} Object with top/right/bottom/left properties.
18246 		 * @private
18247 		 */
18248 		parseBox: function(value) {
18249 			var len, radix = 10;
18250 
18251 			if (!value) {
18252 				return;
18253 			}
18254 
18255 			if (typeof(value) === "number") {
18256 				value = value || 0;
18257 
18258 				return {
18259 					top: value,
18260 					left: value,
18261 					bottom: value,
18262 					right: value
18263 				};
18264 			}
18265 
18266 			value = value.split(' ');
18267 			len = value.length;
18268 
18269 			if (len === 1) {
18270 				value[1] = value[2] = value[3] = value[0];
18271 			} else if (len === 2) {
18272 				value[2] = value[0];
18273 				value[3] = value[1];
18274 			} else if (len === 3) {
18275 				value[3] = value[1];
18276 			}
18277 
18278 			return {
18279 				top: parseInt(value[0], radix) || 0,
18280 				right: parseInt(value[1], radix) || 0,
18281 				bottom: parseInt(value[2], radix) || 0,
18282 				left: parseInt(value[3], radix) || 0
18283 			};
18284 		},
18285 
18286 		borderBox: function() {
18287 			return this._borderBox;
18288 		},
18289 
18290 		paddingBox: function() {
18291 			return this._paddingBox;
18292 		},
18293 
18294 		marginBox: function() {
18295 			return this._marginBox;
18296 		},
18297 
18298 		measureBox: function(elm, prefix) {
18299 			function getStyle(name) {
18300 				var defaultView = document.defaultView;
18301 
18302 				if (defaultView) {
18303 					// Remove camelcase
18304 					name = name.replace(/[A-Z]/g, function(a) {
18305 						return '-' + a;
18306 					});
18307 
18308 					return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
18309 				}
18310 
18311 				return elm.currentStyle[name];
18312 			}
18313 
18314 			function getSide(name) {
18315 				var val = parseFloat(getStyle(name), 10);
18316 
18317 				return isNaN(val) ? 0 : val;
18318 			}
18319 
18320 			return {
18321 				top: getSide(prefix + "TopWidth"),
18322 				right: getSide(prefix + "RightWidth"),
18323 				bottom: getSide(prefix + "BottomWidth"),
18324 				left: getSide(prefix + "LeftWidth")
18325 			};
18326 		},
18327 
18328 		/**
18329 		 * Initializes the current controls layout rect.
18330 		 * This will be executed by the layout managers to determine the
18331 		 * default minWidth/minHeight etc.
18332 		 *
18333 		 * @method initLayoutRect
18334 		 * @return {Object} Layout rect instance.
18335 		 */
18336 		initLayoutRect: function() {
18337 			var self = this, settings = self.settings, borderBox, layoutRect;
18338 			var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
18339 			var startMinWidth, startMinHeight, initialSize;
18340 
18341 			// Measure the current element
18342 			borderBox = self._borderBox = self._borderBox || self.measureBox(elm, 'border');
18343 			self._paddingBox = self._paddingBox || self.measureBox(elm, 'padding');
18344 			self._marginBox = self._marginBox || self.measureBox(elm, 'margin');
18345 			initialSize = DomUtils.getSize(elm);
18346 
18347 			// Setup minWidth/minHeight and width/height
18348 			startMinWidth = settings.minWidth;
18349 			startMinHeight = settings.minHeight;
18350 			minWidth = startMinWidth || initialSize.width;
18351 			minHeight = startMinHeight || initialSize.height;
18352 			width = settings.width;
18353 			height = settings.height;
18354 			autoResize = settings.autoResize;
18355 			autoResize = typeof(autoResize) != "undefined" ? autoResize : !width && !height;
18356 
18357 			width = width || minWidth;
18358 			height = height || minHeight;
18359 
18360 			var deltaW = borderBox.left + borderBox.right;
18361 			var deltaH = borderBox.top + borderBox.bottom;
18362 
18363 			var maxW = settings.maxWidth || 0xFFFF;
18364 			var maxH = settings.maxHeight || 0xFFFF;
18365 
18366 			// Setup initial layout rect
18367 			self._layoutRect = layoutRect = {
18368 				x: settings.x || 0,
18369 				y: settings.y || 0,
18370 				w: width,
18371 				h: height,
18372 				deltaW: deltaW,
18373 				deltaH: deltaH,
18374 				contentW: width - deltaW,
18375 				contentH: height - deltaH,
18376 				innerW: width - deltaW,
18377 				innerH: height - deltaH,
18378 				startMinWidth: startMinWidth || 0,
18379 				startMinHeight: startMinHeight || 0,
18380 				minW: Math.min(minWidth, maxW),
18381 				minH: Math.min(minHeight, maxH),
18382 				maxW: maxW,
18383 				maxH: maxH,
18384 				autoResize: autoResize,
18385 				scrollW: 0
18386 			};
18387 
18388 			self._lastLayoutRect = {};
18389 
18390 			return layoutRect;
18391 		},
18392 
18393 		/**
18394 		 * Getter/setter for the current layout rect.
18395 		 *
18396 		 * @method layoutRect
18397 		 * @param {Object} [newRect] Optional new layout rect.
18398 		 * @return {tinymce.ui.Control/Object} Current control or rect object.
18399 		 */
18400 		layoutRect: function(newRect) {
18401 			var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
18402 
18403 			// Initialize default layout rect
18404 			if (!curRect) {
18405 				curRect = self.initLayoutRect();
18406 			}
18407 
18408 			// Set new rect values
18409 			if (newRect) {
18410 				// Calc deltas between inner and outer sizes
18411 				deltaWidth = curRect.deltaW;
18412 				deltaHeight = curRect.deltaH;
18413 
18414 				// Set x position
18415 				if (newRect.x !== undef) {
18416 					curRect.x = newRect.x;
18417 				}
18418 
18419 				// Set y position
18420 				if (newRect.y !== undef) {
18421 					curRect.y = newRect.y;
18422 				}
18423 
18424 				// Set minW
18425 				if (newRect.minW !== undef) {
18426 					curRect.minW = newRect.minW;
18427 				}
18428 
18429 				// Set minH
18430 				if (newRect.minH !== undef) {
18431 					curRect.minH = newRect.minH;
18432 				}
18433 
18434 				// Set new width and calculate inner width
18435 				size = newRect.w;
18436 				if (size !== undef) {
18437 					size = size < curRect.minW ? curRect.minW : size;
18438 					size = size > curRect.maxW ? curRect.maxW : size;
18439 					curRect.w = size;
18440 					curRect.innerW = size - deltaWidth;
18441 				}
18442 
18443 				// Set new height and calculate inner height
18444 				size = newRect.h;
18445 				if (size !== undef) {
18446 					size = size < curRect.minH ? curRect.minH : size;
18447 					size = size > curRect.maxH ? curRect.maxH : size;
18448 					curRect.h = size;
18449 					curRect.innerH = size - deltaHeight;
18450 				}
18451 
18452 				// Set new inner width and calculate width
18453 				size = newRect.innerW;
18454 				if (size !== undef) {
18455 					size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
18456 					size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
18457 					curRect.innerW = size;
18458 					curRect.w = size + deltaWidth;
18459 				}
18460 
18461 				// Set new height and calculate inner height
18462 				size = newRect.innerH;
18463 				if (size !== undef) {
18464 					size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
18465 					size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
18466 					curRect.innerH = size;
18467 					curRect.h = size + deltaHeight;
18468 				}
18469 
18470 				// Set new contentW
18471 				if (newRect.contentW !== undef) {
18472 					curRect.contentW = newRect.contentW;
18473 				}
18474 
18475 				// Set new contentH
18476 				if (newRect.contentH !== undef) {
18477 					curRect.contentH = newRect.contentH;
18478 				}
18479 
18480 				// Compare last layout rect with the current one to see if we need to repaint or not
18481 				lastLayoutRect = self._lastLayoutRect;
18482 				if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
18483 					lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
18484 					repaintControls = Control.repaintControls;
18485 
18486 					if (repaintControls) {
18487 						if (repaintControls.map && !repaintControls.map[self._id]) {
18488 							repaintControls.push(self);
18489 							repaintControls.map[self._id] = true;
18490 						}
18491 					}
18492 
18493 					lastLayoutRect.x = curRect.x;
18494 					lastLayoutRect.y = curRect.y;
18495 					lastLayoutRect.w = curRect.w;
18496 					lastLayoutRect.h = curRect.h;
18497 				}
18498 
18499 				return self;
18500 			}
18501 
18502 			return curRect;
18503 		},
18504 
18505 		/**
18506 		 * Repaints the control after a layout operation.
18507 		 *
18508 		 * @method repaint
18509 		 */
18510 		repaint: function() {
18511 			var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect, round;
18512 
18513 			// Use Math.round on all values on IE < 9
18514 			round = !document.createRange ? Math.round : function(value) {
18515 				return value;
18516 			};
18517 
18518 			style = self.getEl().style;
18519 			rect = self._layoutRect;
18520 			lastRepaintRect = self._lastRepaintRect || {};
18521 
18522 			borderBox = self._borderBox;
18523 			borderW = borderBox.left + borderBox.right;
18524 			borderH = borderBox.top + borderBox.bottom;
18525 
18526 			if (rect.x !== lastRepaintRect.x) {
18527 				style.left = round(rect.x) + 'px';
18528 				lastRepaintRect.x = rect.x;
18529 			}
18530 
18531 			if (rect.y !== lastRepaintRect.y) {
18532 				style.top = round(rect.y) + 'px';
18533 				lastRepaintRect.y = rect.y;
18534 			}
18535 
18536 			if (rect.w !== lastRepaintRect.w) {
18537 				style.width = round(rect.w - borderW) + 'px';
18538 				lastRepaintRect.w = rect.w;
18539 			}
18540 
18541 			if (rect.h !== lastRepaintRect.h) {
18542 				style.height = round(rect.h - borderH) + 'px';
18543 				lastRepaintRect.h = rect.h;
18544 			}
18545 
18546 			// Update body if needed
18547 			if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
18548 				bodyStyle = self.getEl('body').style;
18549 				bodyStyle.width = round(rect.innerW) + 'px';
18550 				lastRepaintRect.innerW = rect.innerW;
18551 			}
18552 
18553 			if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
18554 				bodyStyle = bodyStyle || self.getEl('body').style;
18555 				bodyStyle.height = round(rect.innerH) + 'px';
18556 				lastRepaintRect.innerH = rect.innerH;
18557 			}
18558 
18559 			self._lastRepaintRect = lastRepaintRect;
18560 			self.fire('repaint', {}, false);
18561 		},
18562 
18563 		/**
18564 		 * Binds a callback to the specified event. This event can both be
18565 		 * native browser events like "click" or custom ones like PostRender.
18566 		 *
18567 		 * The callback function will be passed a DOM event like object that enables yout do stop propagation.
18568 		 *
18569 		 * @method on
18570 		 * @param {String} name Name of the event to bind. For example "click".
18571 		 * @param {String/function} callback Callback function to execute ones the event occurs.
18572 		 * @return {tinymce.ui.Control} Current control object.
18573 		 */
18574 		on: function(name, callback) {
18575 			var self = this;
18576 
18577 			function resolveCallbackName(name) {
18578 				var callback, scope;
18579 
18580 				if (typeof(name) != 'string') {
18581 					return name;
18582 				}
18583 
18584 				return function(e) {
18585 					if (!callback) {
18586 						self.parentsAndSelf().each(function(ctrl) {
18587 							var callbacks = ctrl.settings.callbacks;
18588 
18589 							if (callbacks && (callback = callbacks[name])) {
18590 								scope = ctrl;
18591 								return false;
18592 							}
18593 						});
18594 					}
18595 
18596 					return callback.call(scope, e);
18597 				};
18598 			}
18599 
18600 			getEventDispatcher(self).on(name, resolveCallbackName(callback));
18601 
18602 			return self;
18603 		},
18604 
18605 		/**
18606 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
18607 		 * parameter all event handlers will be removed. If you omit the callback all event handles
18608 		 * by the specified name will be removed.
18609 		 *
18610 		 * @method off
18611 		 * @param {String} [name] Name for the event to unbind.
18612 		 * @param {function} [callback] Callback function to unbind.
18613 		 * @return {mxex.ui.Control} Current control object.
18614 		 */
18615 		off: function(name, callback) {
18616 			getEventDispatcher(this).off(name, callback);
18617 			return this;
18618 		},
18619 
18620 		/**
18621 		 * Fires the specified event by name and arguments on the control. This will execute all
18622 		 * bound event handlers.
18623 		 *
18624 		 * @method fire
18625 		 * @param {String} name Name of the event to fire.
18626 		 * @param {Object} [args] Arguments to pass to the event.
18627 		 * @param {Boolean} [bubble] Value to control bubbeling. Defaults to true.
18628 		 * @return {Object} Current arguments object.
18629 		 */
18630 		fire: function(name, args, bubble) {
18631 			var self = this;
18632 
18633 			args = args || {};
18634 
18635 			if (!args.control) {
18636 				args.control = self;
18637 			}
18638 
18639 			args = getEventDispatcher(self).fire(name, args);
18640 
18641 			// Bubble event up to parents
18642 			if (bubble !== false && self.parent) {
18643 				var parent = self.parent();
18644 				while (parent && !args.isPropagationStopped()) {
18645 					parent.fire(name, args, false);
18646 					parent = parent.parent();
18647 				}
18648 			}
18649 
18650 			return args;
18651 		},
18652 
18653 		/**
18654 		 * Returns true/false if the specified event has any listeners.
18655 		 *
18656 		 * @method hasEventListeners
18657 		 * @param {String} name Name of the event to check for.
18658 		 * @return {Boolean} True/false state if the event has listeners.
18659 		 */
18660 		hasEventListeners: function(name) {
18661 			return getEventDispatcher(this).has(name);
18662 		},
18663 
18664 		/**
18665 		 * Returns a control collection with all parent controls.
18666 		 *
18667 		 * @method parents
18668 		 * @param {String} selector Optional selector expression to find parents.
18669 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
18670 		 */
18671 		parents: function(selector) {
18672 			var self = this, ctrl, parents = new Collection();
18673 
18674 			// Add each parent to collection
18675 			for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) {
18676 				parents.add(ctrl);
18677 			}
18678 
18679 			// Filter away everything that doesn't match the selector
18680 			if (selector) {
18681 				parents = parents.filter(selector);
18682 			}
18683 
18684 			return parents;
18685 		},
18686 
18687 		/**
18688 		 * Returns the current control and it's parents.
18689 		 *
18690 		 * @method parentsAndSelf
18691 		 * @param {String} selector Optional selector expression to find parents.
18692 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
18693 		 */
18694 		parentsAndSelf: function(selector) {
18695 			return new Collection(this).add(this.parents(selector));
18696 		},
18697 
18698 		/**
18699 		 * Returns the control next to the current control.
18700 		 *
18701 		 * @method next
18702 		 * @return {tinymce.ui.Control} Next control instance.
18703 		 */
18704 		next: function() {
18705 			var parentControls = this.parent().items();
18706 
18707 			return parentControls[parentControls.indexOf(this) + 1];
18708 		},
18709 
18710 		/**
18711 		 * Returns the control previous to the current control.
18712 		 *
18713 		 * @method prev
18714 		 * @return {tinymce.ui.Control} Previous control instance.
18715 		 */
18716 		prev: function() {
18717 			var parentControls = this.parent().items();
18718 
18719 			return parentControls[parentControls.indexOf(this) - 1];
18720 		},
18721 
18722 		/**
18723 		 * Find the common ancestor for two control instances.
18724 		 *
18725 		 * @method findCommonAncestor
18726 		 * @param {tinymce.ui.Control} ctrl1 First control.
18727 		 * @param {tinymce.ui.Control} ctrl2 Second control.
18728 		 * @return {tinymce.ui.Control} Ancestor control instance.
18729 		 */
18730 		findCommonAncestor: function(ctrl1, ctrl2) {
18731 			var parentCtrl;
18732 
18733 			while (ctrl1) {
18734 				parentCtrl = ctrl2;
18735 
18736 				while (parentCtrl && ctrl1 != parentCtrl) {
18737 					parentCtrl = parentCtrl.parent();
18738 				}
18739 
18740 				if (ctrl1 == parentCtrl) {
18741 					break;
18742 				}
18743 
18744 				ctrl1 = ctrl1.parent();
18745 			}
18746 
18747 			return ctrl1;
18748 		},
18749 
18750 		/**
18751 		 * Returns true/false if the specific control has the specific class.
18752 		 *
18753 		 * @method hasClass
18754 		 * @param {String} cls Class to check for.
18755 		 * @param {String} [group] Sub element group name.
18756 		 * @return {Boolean} True/false if the control has the specified class.
18757 		 */
18758 		hasClass: function(cls, group) {
18759 			var classes = this._classes[group || 'control'];
18760 
18761 			cls = this.classPrefix + cls;
18762 
18763 			return classes && !!classes.map[cls];
18764 		},
18765 
18766 		/**
18767 		 * Adds the specified class to the control
18768 		 *
18769 		 * @method addClass
18770 		 * @param {String} cls Class to check for.
18771 		 * @param {String} [group] Sub element group name.
18772 		 * @return {tinymce.ui.Control} Current control object.
18773 		 */
18774 		addClass: function(cls, group) {
18775 			var self = this, classes, elm;
18776 
18777 			cls = this.classPrefix + cls;
18778 			classes = self._classes[group || 'control'];
18779 
18780 			if (!classes) {
18781 				classes = [];
18782 				classes.map = {};
18783 				self._classes[group || 'control'] = classes;
18784 			}
18785 
18786 			if (!classes.map[cls]) {
18787 				classes.map[cls] = cls;
18788 				classes.push(cls);
18789 
18790 				if (self._rendered) {
18791 					elm = self.getEl(group);
18792 
18793 					if (elm) {
18794 						elm.className = classes.join(' ');
18795 					}
18796 				}
18797 			}
18798 
18799 			return self;
18800 		},
18801 
18802 		/**
18803 		 * Removes the specified class from the control.
18804 		 *
18805 		 * @method removeClass
18806 		 * @param {String} cls Class to remove.
18807 		 * @param {String} [group] Sub element group name.
18808 		 * @return {tinymce.ui.Control} Current control object.
18809 		 */
18810 		removeClass: function(cls, group) {
18811 			var self = this, classes, i, elm;
18812 
18813 			cls = this.classPrefix + cls;
18814 			classes = self._classes[group || 'control'];
18815 			if (classes && classes.map[cls]) {
18816 				delete classes.map[cls];
18817 
18818 				i = classes.length;
18819 				while (i--) {
18820 					if (classes[i] === cls) {
18821 						classes.splice(i, 1);
18822 					}
18823 				}
18824 			}
18825 
18826 			if (self._rendered) {
18827 				elm = self.getEl(group);
18828 
18829 				if (elm) {
18830 					elm.className = classes.join(' ');
18831 				}
18832 			}
18833 
18834 			return self;
18835 		},
18836 
18837 		/**
18838 		 * Toggles the specified class on the control.
18839 		 *
18840 		 * @method toggleClass
18841 		 * @param {String} cls Class to remove.
18842 		 * @param {Boolean} state True/false state to add/remove class.
18843 		 * @param {String} [group] Sub element group name.
18844 		 * @return {tinymce.ui.Control} Current control object.
18845 		 */
18846 		toggleClass: function(cls, state, group) {
18847 			var self = this;
18848 
18849 			if (state) {
18850 				self.addClass(cls, group);
18851 			} else {
18852 				self.removeClass(cls, group);
18853 			}
18854 
18855 			return self;
18856 		},
18857 
18858 		/**
18859 		 * Returns the class string for the specified group name.
18860 		 *
18861 		 * @method classes
18862 		 * @param {String} [group] Group to get clases by.
18863 		 * @return {String} Classes for the specified group.
18864 		 */
18865 		classes: function(group) {
18866 			var classes = this._classes[group || 'control'];
18867 
18868 			return classes ? classes.join(' ') : '';
18869 		},
18870 
18871 		/**
18872 		 * Sets the inner HTML of the control element.
18873 		 *
18874 		 * @method innerHtml
18875 		 * @param {String} html Html string to set as inner html.
18876 		 * @return {tinymce.ui.Control} Current control object.
18877 		 */
18878 		innerHtml: function(html) {
18879 			DomUtils.innerHtml(this.getEl(), html);
18880 			return this;
18881 		},
18882 
18883 		/**
18884 		 * Returns the control DOM element or sub element.
18885 		 *
18886 		 * @method getEl
18887 		 * @param {String} [suffix] Suffix to get element by.
18888 		 * @param {Boolean} [dropCache] True if the cache for the element should be dropped.
18889 		 * @return {Element} HTML DOM element for the current control or it's children.
18890 		 */
18891 		getEl: function(suffix, dropCache) {
18892 			var elm, id = suffix ? this._id + '-' + suffix : this._id;
18893 
18894 			elm = elementIdCache[id] = (dropCache === true ? null : elementIdCache[id]) || DomUtils.get(id);
18895 
18896 			return elm;
18897 		},
18898 
18899 		/**
18900 		 * Sets/gets the visible for the control.
18901 		 *
18902 		 * @method visible
18903 		 * @param {Boolean} state Value to set to control.
18904 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
18905 		 */
18906 		visible: function(state) {
18907 			var self = this, parentCtrl;
18908 
18909 			if (typeof(state) !== "undefined") {
18910 				if (self._visible !== state) {
18911 					if (self._rendered) {
18912 						self.getEl().style.display = state ? '' : 'none';
18913 					}
18914 
18915 					self._visible = state;
18916 
18917 					// Parent container needs to reflow
18918 					parentCtrl = self.parent();
18919 					if (parentCtrl) {
18920 						parentCtrl._lastRect = null;
18921 					}
18922 
18923 					self.fire(state ? 'show' : 'hide');
18924 				}
18925 
18926 				return self;
18927 			}
18928 
18929 			return self._visible;
18930 		},
18931 
18932 		/**
18933 		 * Sets the visible state to true.
18934 		 *
18935 		 * @method show
18936 		 * @return {tinymce.ui.Control} Current control instance.
18937 		 */
18938 		show: function() {
18939 			return this.visible(true);
18940 		},
18941 
18942 		/**
18943 		 * Sets the visible state to false.
18944 		 *
18945 		 * @method hide
18946 		 * @return {tinymce.ui.Control} Current control instance.
18947 		 */
18948 		hide: function() {
18949 			return this.visible(false);
18950 		},
18951 
18952 		/**
18953 		 * Focuses the current control.
18954 		 *
18955 		 * @method focus
18956 		 * @return {tinymce.ui.Control} Current control instance.
18957 		 */
18958 		focus: function() {
18959 			try {
18960 				this.getEl().focus();
18961 			} catch (ex) {
18962 				// Ignore IE error
18963 			}
18964 
18965 			return this;
18966 		},
18967 
18968 		/**
18969 		 * Blurs the current control.
18970 		 *
18971 		 * @method blur
18972 		 * @return {tinymce.ui.Control} Current control instance.
18973 		 */
18974 		blur: function() {
18975 			this.getEl().blur();
18976 
18977 			return this;
18978 		},
18979 
18980 		/**
18981 		 * Sets the specified aria property.
18982 		 *
18983 		 * @method aria
18984 		 * @param {String} name Name of the aria property to set.
18985 		 * @param {String} value Value of the aria property.
18986 		 * @return {tinymce.ui.Control} Current control instance.
18987 		 */
18988 		aria: function(name, value) {
18989 			var self = this, elm = self.getEl(self.ariaTarget);
18990 
18991 			if (typeof(value) === "undefined") {
18992 				return self._aria[name];
18993 			} else {
18994 				self._aria[name] = value;
18995 			}
18996 
18997 			if (self._rendered) {
18998 				elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
18999 			}
19000 
19001 			return self;
19002 		},
19003 
19004 		/**
19005 		 * Encodes the specified string with HTML entities. It will also
19006 		 * translate the string to different languages.
19007 		 *
19008 		 * @method encode
19009 		 * @param {String/Object/Array} text Text to entity encode.
19010 		 * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
19011 		 * @return {String} Encoded and possible traslated string. 
19012 		 */
19013 		encode: function(text, translate) {
19014 			if (translate !== false) {
19015 				text = this.translate(text);
19016 			}
19017 
19018 			return (text || '').replace(/[&<>"]/g, function(match) {
19019 				return '&#' + match.charCodeAt(0) + ';';
19020 			});
19021 		},
19022 
19023 		/**
19024 		 * Returns the translated string.
19025 		 *
19026 		 * @method translate
19027 		 * @param {String} text Text to translate.
19028 		 * @return {String} Translated string or the same as the input.
19029 		 */
19030 		translate: function(text) {
19031 			return Control.translate ? Control.translate(text) : text;
19032 		},
19033 
19034 		/**
19035 		 * Adds items before the current control.
19036 		 *
19037 		 * @method before
19038 		 * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
19039 		 * @return {tinymce.ui.Control} Current control instance.
19040 		 */
19041 		before: function(items) {
19042 			var self = this, parent = self.parent();
19043 
19044 			if (parent) {
19045 				parent.insert(items, parent.items().indexOf(self), true);
19046 			}
19047 
19048 			return self;
19049 		},
19050 
19051 		/**
19052 		 * Adds items after the current control.
19053 		 *
19054 		 * @method after
19055 		 * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
19056 		 * @return {tinymce.ui.Control} Current control instance.
19057 		 */
19058 		after: function(items) {
19059 			var self = this, parent = self.parent();
19060 
19061 			if (parent) {
19062 				parent.insert(items, parent.items().indexOf(self));
19063 			}
19064 
19065 			return self;
19066 		},
19067 
19068 		/**
19069 		 * Removes the current control from DOM and from UI collections.
19070 		 *
19071 		 * @method remove
19072 		 * @return {tinymce.ui.Control} Current control instance.
19073 		 */
19074 		remove: function() {
19075 			var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
19076 
19077 			if (self.items) {
19078 				var controls = self.items().toArray();
19079 				i = controls.length;
19080 				while (i--) {
19081 					controls[i].remove();
19082 				}
19083 			}
19084 
19085 			if (parent && parent.items) {
19086 				newItems = [];
19087 
19088 				parent.items().each(function(item) {
19089 					if (item !== self) {
19090 						newItems.push(item);
19091 					}
19092 				});
19093 
19094 				parent.items().set(newItems);
19095 				parent._lastRect = null;
19096 			}
19097 
19098 			if (self._eventsRoot && self._eventsRoot == self) {
19099 				DomUtils.off(elm);
19100 			}
19101 
19102 			var lookup = self.getRoot().controlIdLookup;
19103 			if (lookup) {
19104 				delete lookup[self._id];
19105 			}
19106 
19107 			delete elementIdCache[self._id];
19108 
19109 			if (elm && elm.parentNode) {
19110 				var nodes = elm.getElementsByTagName('*');
19111 
19112 				i = nodes.length;
19113 				while (i--) {
19114 					delete elementIdCache[nodes[i].id];
19115 				}
19116 
19117 				elm.parentNode.removeChild(elm);
19118 			}
19119 
19120 			self._rendered = false;
19121 
19122 			return self;
19123 		},
19124 
19125 		/**
19126 		 * Renders the control before the specified element.
19127 		 *
19128 		 * @method renderBefore
19129 		 * @param {Element} elm Element to render before.
19130 		 * @return {tinymce.ui.Control} Current control instance.
19131 		 */
19132 		renderBefore: function(elm) {
19133 			var self = this;
19134 
19135 			elm.parentNode.insertBefore(DomUtils.createFragment(self.renderHtml()), elm);
19136 			self.postRender();
19137 
19138 			return self;
19139 		},
19140 
19141 		/**
19142 		 * Renders the control to the specified element.
19143 		 *
19144 		 * @method renderBefore
19145 		 * @param {Element} elm Element to render to.
19146 		 * @return {tinymce.ui.Control} Current control instance.
19147 		 */
19148 		renderTo: function(elm) {
19149 			var self = this;
19150 
19151 			elm = elm || self.getContainerElm();
19152 			elm.appendChild(DomUtils.createFragment(self.renderHtml()));
19153 			self.postRender();
19154 
19155 			return self;
19156 		},
19157 
19158 		/**
19159 		 * Post render method. Called after the control has been rendered to the target.
19160 		 *
19161 		 * @method postRender
19162 		 * @return {tinymce.ui.Control} Current control instance.
19163 		 */
19164 		postRender: function() {
19165 			var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
19166 
19167 			// Bind on<event> settings
19168 			for (name in settings) {
19169 				if (name.indexOf("on") === 0) {
19170 					self.on(name.substr(2), settings[name]);
19171 				}
19172 			}
19173 
19174 			if (self._eventsRoot) {
19175 				for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
19176 					parentEventsRoot = parent._eventsRoot;
19177 				}
19178 
19179 				if (parentEventsRoot) {
19180 					for (name in parentEventsRoot._nativeEvents) {
19181 						self._nativeEvents[name] = true;
19182 					}
19183 				}
19184 			}
19185 
19186 			self.bindPendingEvents();
19187 
19188 			if (settings.style) {
19189 				elm = self.getEl();
19190 				if (elm) {
19191 					elm.setAttribute('style', settings.style);
19192 					elm.style.cssText = settings.style;
19193 				}
19194 			}
19195 
19196 			if (!self._visible) {
19197 				DomUtils.css(self.getEl(), 'display', 'none');
19198 			}
19199 
19200 			if (self.settings.border) {
19201 				box = self.borderBox();
19202 				DomUtils.css(self.getEl(), {
19203 					'border-top-width': box.top,
19204 					'border-right-width': box.right,
19205 					'border-bottom-width': box.bottom,
19206 					'border-left-width': box.left
19207 				});
19208 			}
19209 
19210 			// Add instance to lookup
19211 			var root = self.getRoot();
19212 			if (!root.controlIdLookup) {
19213 				root.controlIdLookup = {};
19214 			}
19215 
19216 			root.controlIdLookup[self._id] = self;
19217 
19218 			for (var key in self._aria) {
19219 				self.aria(key, self._aria[key]);
19220 			}
19221 
19222 			self.fire('postrender', {}, false);
19223 		},
19224 
19225 		/**
19226 		 * Scrolls the current control into view.
19227 		 *
19228 		 * @method scrollIntoView
19229 		 * @param {String} align Alignment in view top|center|bottom.
19230 		 * @return {tinymce.ui.Control} Current control instance.
19231 		 */
19232 		scrollIntoView: function(align) {
19233 			function getOffset(elm, rootElm) {
19234 				var x, y, parent = elm;
19235 
19236 				x = y = 0;
19237 				while (parent && parent != rootElm && parent.nodeType) {
19238 					x += parent.offsetLeft || 0;
19239 					y += parent.offsetTop || 0;
19240 					parent = parent.offsetParent;
19241 				}
19242 
19243 				return {x: x, y: y};
19244 			}
19245 
19246 			var elm = this.getEl(), parentElm = elm.parentNode;
19247 			var x, y, width, height, parentWidth, parentHeight;
19248 			var pos = getOffset(elm, parentElm);
19249 
19250 			x = pos.x;
19251 			y = pos.y;
19252 			width = elm.offsetWidth;
19253 			height = elm.offsetHeight;
19254 			parentWidth = parentElm.clientWidth;
19255 			parentHeight = parentElm.clientHeight;
19256 
19257 			if (align == "end") {
19258 				x -= parentWidth - width;
19259 				y -= parentHeight - height;
19260 			} else if (align == "center") {
19261 				x -= (parentWidth / 2) - (width / 2);
19262 				y -= (parentHeight / 2) - (height / 2);
19263 			}
19264 
19265 			parentElm.scrollLeft = x;
19266 			parentElm.scrollTop = y;
19267 
19268 			return this;
19269 		},
19270 
19271 		/**
19272 		 * Binds pending DOM events.
19273 		 *
19274 		 * @private
19275 		 */
19276 		bindPendingEvents: function() {
19277 			var self = this, i, l, parents, eventRootCtrl, nativeEvents, name;
19278 
19279 			function delegate(e) {
19280 				var control = self.getParentCtrl(e.target);
19281 
19282 				if (control) {
19283 					control.fire(e.type, e);
19284 				}
19285 			}
19286 
19287 			function mouseLeaveHandler() {
19288 				var ctrl = eventRootCtrl._lastHoverCtrl;
19289 
19290 				if (ctrl) {
19291 					ctrl.fire("mouseleave", {target: ctrl.getEl()});
19292 
19293 					ctrl.parents().each(function(ctrl) {
19294 						ctrl.fire("mouseleave", {target: ctrl.getEl()});
19295 					});
19296 
19297 					eventRootCtrl._lastHoverCtrl = null;
19298 				}
19299 			}
19300 
19301 			function mouseEnterHandler(e) {
19302 				var ctrl = self.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
19303 
19304 				// Over on a new control
19305 				if (ctrl !== lastCtrl) {
19306 					eventRootCtrl._lastHoverCtrl = ctrl;
19307 
19308 					parents = ctrl.parents().toArray().reverse();
19309 					parents.push(ctrl);
19310 
19311 					if (lastCtrl) {
19312 						lastParents = lastCtrl.parents().toArray().reverse();
19313 						lastParents.push(lastCtrl);
19314 
19315 						for (idx = 0; idx < lastParents.length; idx++) {
19316 							if (parents[idx] !== lastParents[idx]) {
19317 								break;
19318 							}
19319 						}
19320 
19321 						for (i = lastParents.length - 1; i >= idx; i--) {
19322 							lastCtrl = lastParents[i];
19323 							lastCtrl.fire("mouseleave", {
19324 								target : lastCtrl.getEl()
19325 							});
19326 						}
19327 					}
19328 
19329 					for (i = idx; i < parents.length; i++) {
19330 						ctrl = parents[i];
19331 						ctrl.fire("mouseenter", {
19332 							target : ctrl.getEl()
19333 						});
19334 					}
19335 				}
19336 			}
19337 
19338 			function fixWheelEvent(e) {
19339 				e.preventDefault();
19340 
19341 				if (e.type == "mousewheel") {
19342 					e.deltaY = -1 / 40 * e.wheelDelta;
19343 
19344 					if (e.wheelDeltaX) {
19345 						e.deltaX = -1 / 40 * e.wheelDeltaX;
19346 					}
19347 				} else {
19348 					e.deltaX = 0;
19349 					e.deltaY = e.detail;
19350 				}
19351 
19352 				e = self.fire("wheel", e);
19353 			}
19354 
19355 			self._rendered = true;
19356 
19357 			nativeEvents = self._nativeEvents;
19358 			if (nativeEvents) {
19359 				// Find event root element if it exists
19360 				parents = self.parents().toArray();
19361 				parents.unshift(self);
19362 				for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
19363 					eventRootCtrl = parents[i]._eventsRoot;
19364 				}
19365 
19366 				// Event root wasn't found the use the root control
19367 				if (!eventRootCtrl) {
19368 					eventRootCtrl = parents[parents.length - 1] || self;
19369 				}
19370 
19371 				// Set the eventsRoot property on children that didn't have it
19372 				self._eventsRoot = eventRootCtrl;
19373 				for (l = i, i = 0; i < l; i++) {
19374 					parents[i]._eventsRoot = eventRootCtrl;
19375 				}
19376 
19377 				var eventRootDelegates = eventRootCtrl._delegates;
19378 				if (!eventRootDelegates) {
19379 					eventRootDelegates = eventRootCtrl._delegates = {};
19380 				}
19381 
19382 				// Bind native event delegates
19383 				for (name in nativeEvents) {
19384 					if (!nativeEvents) {
19385 						return false;
19386 					}
19387 
19388 					if (name === "wheel" && !hasWheelEventSupport) {
19389 						if (hasMouseWheelEventSupport) {
19390 							DomUtils.on(self.getEl(), "mousewheel", fixWheelEvent);
19391 						} else {
19392 							DomUtils.on(self.getEl(), "DOMMouseScroll", fixWheelEvent);
19393 						}
19394 
19395 						continue;
19396 					}
19397 
19398 					// Special treatment for mousenter/mouseleave since these doesn't bubble
19399 					if (name === "mouseenter" || name === "mouseleave") {
19400 						// Fake mousenter/mouseleave
19401 						if (!eventRootCtrl._hasMouseEnter) {
19402 							DomUtils.on(eventRootCtrl.getEl(), "mouseleave", mouseLeaveHandler);
19403 							DomUtils.on(eventRootCtrl.getEl(), "mouseover", mouseEnterHandler);
19404 							eventRootCtrl._hasMouseEnter = 1;
19405 						}
19406 					} else if (!eventRootDelegates[name]) {
19407 						DomUtils.on(eventRootCtrl.getEl(), name, delegate);
19408 						eventRootDelegates[name] = true;
19409 					}
19410 
19411 					// Remove the event once it's bound
19412 					nativeEvents[name] = false;
19413 				}
19414 			}
19415 		},
19416 
19417 		getRoot: function() {
19418 			var ctrl = this, rootControl, parents = [];
19419 
19420 			while (ctrl) {
19421 				if (ctrl.rootControl) {
19422 					rootControl = ctrl.rootControl;
19423 					break;
19424 				}
19425 
19426 				parents.push(ctrl);
19427 				rootControl = ctrl;
19428 				ctrl = ctrl.parent();
19429 			}
19430 
19431 			if (!rootControl) {
19432 				rootControl = this;
19433 			}
19434 
19435 			var i = parents.length;
19436 			while (i--) {
19437 				parents[i].rootControl = rootControl;
19438 			}
19439 
19440 			return rootControl;
19441 		},
19442 
19443 		/**
19444 		 * Reflows the current control and it's parents.
19445 		 * This should be used after you for example append children to the current control so
19446 		 * that the layout managers know that they need to reposition everything.
19447 		 *
19448 		 * @example
19449 		 * container.append({type: 'button', text: 'My button'}).reflow();
19450 		 *
19451 		 * @method reflow
19452 		 * @return {tinymce.ui.Control} Current control instance.
19453 		 */
19454 		reflow: function() {
19455 			this.repaint();
19456 
19457 			return this;
19458 		}
19459 
19460 		/**
19461 		 * Sets/gets the parent container for the control.
19462 		 *
19463 		 * @method parent
19464 		 * @param {tinymce.ui.Container} parent Optional parent to set.
19465 		 * @return {tinymce.ui.Control} Parent control or the current control on a set action.
19466 		 */
19467 		// parent: function(parent) {} -- Generated
19468 
19469 		/**
19470 		 * Sets/gets the text for the control.
19471 		 *
19472 		 * @method text
19473 		 * @param {String} value Value to set to control.
19474 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
19475 		 */
19476 		// text: function(value) {} -- Generated
19477 
19478 		/**
19479 		 * Sets/gets the width for the control.
19480 		 *
19481 		 * @method width
19482 		 * @param {Number} value Value to set to control.
19483 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
19484 		 */
19485 		// width: function(value) {} -- Generated
19486 
19487 		/**
19488 		 * Sets/gets the height for the control.
19489 		 *
19490 		 * @method height
19491 		 * @param {Number} value Value to set to control.
19492 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
19493 		 */
19494 		// height: function(value) {} -- Generated
19495 
19496 		/**
19497 		 * Sets/gets the disabled state on the control.
19498 		 *
19499 		 * @method disabled
19500 		 * @param {Boolean} state Value to set to control.
19501 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
19502 		 */
19503 		// disabled: function(state) {} -- Generated
19504 
19505 		/**
19506 		 * Sets/gets the active for the control.
19507 		 *
19508 		 * @method active
19509 		 * @param {Boolean} state Value to set to control.
19510 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
19511 		 */
19512 		// active: function(state) {} -- Generated
19513 
19514 		/**
19515 		 * Sets/gets the name for the control.
19516 		 *
19517 		 * @method name
19518 		 * @param {String} value Value to set to control.
19519 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
19520 		 */
19521 		// name: function(value) {} -- Generated
19522 
19523 		/**
19524 		 * Sets/gets the title for the control.
19525 		 *
19526 		 * @method title
19527 		 * @param {String} value Value to set to control.
19528 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
19529 		 */
19530 		// title: function(value) {} -- Generated
19531 	});
19532 
19533 	return Control;
19534 });
19535 
19536 // Included from: js/tinymce/classes/ui/Factory.js
19537 
19538 /**
19539  * Factory.js
19540  *
19541  * Copyright, Moxiecode Systems AB
19542  * Released under LGPL License.
19543  *
19544  * License: http://www.tinymce.com/license
19545  * Contributing: http://www.tinymce.com/contributing
19546  */
19547 
19548 /*global tinymce:true */
19549 
19550 /**
19551  * This class is a factory for control instances. This enables you
19552  * to create instances of controls without having to require the UI controls directly.
19553  *
19554  * It also allow you to override or add new control types.
19555  *
19556  * @class tinymce.ui.Factory
19557  */
19558 define("tinymce/ui/Factory", [], function() {
19559 	"use strict";
19560 
19561 	var types = {}, namespaceInit;
19562 
19563 	return {
19564 		/**
19565 		 * Adds a new control instance type to the factory.
19566 		 *
19567 		 * @method add
19568 		 * @param {String} type Type name for example "button".
19569 		 * @param {function} typeClass Class type function.
19570 		 */
19571 		add: function(type, typeClass) {
19572 			types[type.toLowerCase()] = typeClass;
19573 		},
19574 
19575 		/**
19576 		 * Returns true/false if the specified type exists or not.
19577 		 *
19578 		 * @method has
19579 		 * @param {String} type Type to look for.
19580 		 * @return {Boolean} true/false if the control by name exists.
19581 		 */
19582 		has: function(type) {
19583 			return !!types[type.toLowerCase()];
19584 		},
19585 
19586 		/**
19587 		 * Creates a new control instance based on the settings provided. The instance created will be
19588 		 * based on the specified type property it can also create whole structures of components out of
19589 		 * the specified JSON object.
19590 		 *
19591 		 * @example
19592 		 * tinymce.ui.Factory.create({
19593 		 *     type: 'button',
19594 		 *     text: 'Hello world!'
19595 		 * });
19596 		 *
19597 		 * @method create
19598 		 * @param {Object/String} settings Name/Value object with items used to create the type.
19599 		 * @return {tinymce.ui.Control} Control instance based on the specified type.
19600 		 */
19601 		create: function(type, settings) {
19602 			var ControlType, name, namespace;
19603 
19604 			// Build type lookup
19605 			if (!namespaceInit) {
19606 				namespace = tinymce.ui;
19607 
19608 				for (name in namespace) {
19609 					types[name.toLowerCase()] = namespace[name];
19610 				}
19611 
19612 				namespaceInit = true;
19613 			}
19614 
19615 			// If string is specified then use it as the type
19616 			if (typeof(type) == 'string') {
19617 				settings = settings || {};
19618 				settings.type = type;
19619 			} else {
19620 				settings = type;
19621 				type = settings.type;
19622 			}
19623 
19624 			// Find control type
19625 			type = type.toLowerCase();
19626 			ControlType = types[type];
19627 
19628 			// #if debug
19629 
19630 			if (!ControlType) {
19631 				throw new Error("Could not find control by type: " + type);
19632 			}
19633 
19634 			// #endif
19635 
19636 			ControlType = new ControlType(settings);
19637 			ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine
19638 
19639 			return ControlType;
19640 		}
19641 	};
19642 });
19643 
19644 // Included from: js/tinymce/classes/ui/KeyboardNavigation.js
19645 
19646 /**
19647  * KeyboardNavigation.js
19648  *
19649  * Copyright, Moxiecode Systems AB
19650  * Released under LGPL License.
19651  *
19652  * License: http://www.tinymce.com/license
19653  * Contributing: http://www.tinymce.com/contributing
19654  */
19655 
19656 /**
19657  * This class handles keyboard navigation of controls and elements.
19658  *
19659  * @class tinymce.ui.KeyboardNavigation
19660  */
19661 define("tinymce/ui/KeyboardNavigation", [
19662 ], function() {
19663 	"use strict";
19664 
19665 	/**
19666 	 * This class handles all keyboard navigation for WAI-ARIA support. Each root container
19667 	 * gets an instance of this class.
19668 	 *
19669 	 * @constructor
19670 	 */
19671 	return function(settings) {
19672 		var root = settings.root, focusedElement, focusedControl;
19673 
19674 		focusedElement = document.activeElement;
19675 		focusedControl = root.getParentCtrl(focusedElement);
19676 
19677 		/**
19678 		 * Returns the currently focused elements wai aria role of the currently
19679 		 * focused element or specified element.
19680 		 *
19681 		 * @private
19682 		 * @param {Element} elm Optional element to get role from.
19683 		 * @return {String} Role of specified element.
19684 		 */
19685 		function getRole(elm) {
19686 			elm = elm || focusedElement;
19687 
19688 			return elm && elm.getAttribute('role');
19689 		}
19690 
19691 		/**
19692 		 * Returns the wai role of the parent element of the currently
19693 		 * focused element or specified element.
19694 		 *
19695 		 * @private
19696 		 * @param {Element} elm Optional element to get parent role from.
19697 		 * @return {String} Role of the first parent that has a role.
19698 		 */
19699 		function getParentRole(elm) {
19700 			var role, parent = elm || focusedElement;
19701 
19702 			while ((parent = parent.parentNode)) {
19703 				if ((role = getRole(parent))) {
19704 					return role;
19705 				}
19706 			}
19707 		}
19708 
19709 		/**
19710 		 * Returns a wai aria property by name for example aria-selected.
19711 		 *
19712 		 * @private
19713 		 * @param {String} name Name of the aria property to get for example "disabled".
19714 		 * @return {String} Aria property value.
19715 		 */
19716 		function getAriaProp(name) {
19717 			var elm = focusedElement;
19718 
19719 			if (elm) {
19720 				return elm.getAttribute('aria-' + name);
19721 			}
19722 		}
19723 
19724 		/**
19725 		 * Is the element a text input element or not.
19726 		 *
19727 		 * @private
19728 		 * @param {Element} elm Element to check if it's an text input element or not.
19729 		 * @return {Boolean} True/false if the element is a text element or not.
19730 		 */
19731 		function isTextInputElement(elm) {
19732 			var tagName = elm.tagName.toUpperCase();
19733 
19734 			// Notice: since type can be "email" etc we don't check the type
19735 			// So all input elements gets treated as text input elements
19736 			return tagName == "INPUT" || tagName == "TEXTAREA";
19737 		}
19738 
19739 		/**
19740 		 * Returns true/false if the specified element can be focused or not.
19741 		 *
19742 		 * @private
19743 		 * @param {Element} elm DOM element to check if it can be focused or not.
19744 		 * @return {Boolean} True/false if the element can have focus.
19745 		 */
19746 		function canFocus(elm) {
19747 			if (isTextInputElement(elm) && !elm.hidden) {
19748 				return true;
19749 			}
19750 
19751 			if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(getRole(elm))) {
19752 				return true;
19753 			}
19754 
19755 			return false;
19756 		}
19757 
19758 		/**
19759 		 * Returns an array of focusable visible elements within the specified container element.
19760 		 *
19761 		 * @private
19762 		 * @param {Element} elm DOM element to find focusable elements within.
19763 		 * @return {Array} Array of focusable elements.
19764 		 */
19765 		function getFocusElements(elm) {
19766 			var elements = [];
19767 
19768 			function collect(elm) {
19769 				if (elm.nodeType != 1 || elm.style.display == 'none') {
19770 					return;
19771 				}
19772 
19773 				if (canFocus(elm)) {
19774 					elements.push(elm);
19775 				}
19776 
19777 				for (var i = 0; i < elm.childNodes.length; i++) {
19778 					collect(elm.childNodes[i]);
19779 				}
19780 			}
19781 
19782 			collect(elm || root.getEl());
19783 
19784 			return elements;
19785 		}
19786 
19787 		/**
19788 		 * Returns the navigation root control for the specified control. The navigation root
19789 		 * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group.
19790 		 * It will look for parents of the specified target control or the currenty focused control if this option is omitted.
19791 		 *
19792 		 * @private
19793 		 * @param {tinymce.ui.Control} targetControl Optional target control to find root of.
19794 		 * @return {tinymce.ui.Control} Navigation root control.
19795 		 */
19796 		function getNavigationRoot(targetControl) {
19797 			var navigationRoot, controls;
19798 
19799 			targetControl = targetControl || focusedControl;
19800 			controls = targetControl.parents().toArray();
19801 			controls.unshift(targetControl);
19802 
19803 			for (var i = 0; i < controls.length; i++) {
19804 				navigationRoot = controls[i];
19805 
19806 				if (navigationRoot.settings.ariaRoot) {
19807 					break;
19808 				}
19809 			}
19810 
19811 			return navigationRoot;
19812 		}
19813 
19814 		/**
19815 		 * Focuses the first item in the specified targetControl element or the last aria index if the
19816 		 * navigation root has the ariaRemember option enabled.
19817 		 *
19818 		 * @private
19819 		 * @param {tinymce.ui.Control} targetControl Target control to focus the first item in.
19820 		 */
19821 		function focusFirst(targetControl) {
19822 			var navigationRoot = getNavigationRoot(targetControl);
19823 			var focusElements = getFocusElements(navigationRoot.getEl());
19824 
19825 			if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) {
19826 				moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements);
19827 			} else {
19828 				moveFocusToIndex(0, focusElements);
19829 			}
19830 		}
19831 
19832 		/**
19833 		 * Moves the focus to the specified index within the elements list.
19834 		 * This will scope the index to the size of the element list if it changed.
19835 		 *
19836 		 * @private
19837 		 * @param {Number} idx Specified index to move to.
19838 		 * @param {Array} elements Array with dom elements to move focus within.
19839 		 * @return {Number} Input index or a changed index if it was out of range.
19840 		 */
19841 		function moveFocusToIndex(idx, elements) {
19842 			if (idx < 0) {
19843 				idx = elements.length - 1;
19844 			} else if (idx >= elements.length) {
19845 				idx = 0;
19846 			}
19847 
19848 			if (elements[idx]) {
19849 				elements[idx].focus();
19850 			}
19851 
19852 			return idx;
19853 		}
19854 
19855 		/**
19856 		 * Moves the focus forwards or backwards.
19857 		 *
19858 		 * @private
19859 		 * @param {Number} dir Direction to move in positive means forward, negative means backwards.
19860 		 * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements.
19861 		 */
19862 		function moveFocus(dir, elements) {
19863 			var idx = -1, navigationRoot = getNavigationRoot();
19864 
19865 			elements = elements || getFocusElements(navigationRoot.getEl());
19866 
19867 			for (var i = 0; i < elements.length; i++) {
19868 				if (elements[i] === focusedElement) {
19869 					idx = i;
19870 				}
19871 			}
19872 
19873 			idx += dir;
19874 			navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements);
19875 		}
19876 
19877 		/**
19878 		 * Moves the focus to the left this is called by the left key.
19879 		 *
19880 		 * @private
19881 		 */
19882 		function left() {
19883 			var parentRole = getParentRole();
19884 
19885 			if (parentRole == "tablist") {
19886 				moveFocus(-1, getFocusElements(focusedElement.parentNode));
19887 			} else if (focusedControl.parent().submenu) {
19888 				cancel();
19889 			} else {
19890 				moveFocus(-1);
19891 			}
19892 		}
19893 
19894 		/**
19895 		 * Moves the focus to the right this is called by the right key.
19896 		 *
19897 		 * @private
19898 		 */
19899 		function right() {
19900 			var role = getRole(), parentRole = getParentRole();
19901 
19902 			if (parentRole == "tablist") {
19903 				moveFocus(1, getFocusElements(focusedElement.parentNode));
19904 			} else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) {
19905 				enter();
19906 			} else {
19907 				moveFocus(1);
19908 			}
19909 		}
19910 
19911 		/**
19912 		 * Moves the focus to the up this is called by the up key.
19913 		 *
19914 		 * @private
19915 		 */
19916 		function up() {
19917 			moveFocus(-1);
19918 		}
19919 
19920 		/**
19921 		 * Moves the focus to the up this is called by the down key.
19922 		 *
19923 		 * @private
19924 		 */
19925 		function down() {
19926 			var role = getRole(), parentRole = getParentRole();
19927 
19928 			if (role == "menuitem" && parentRole == "menubar") {
19929 				enter();
19930 			} else if (role == "button" && getAriaProp('haspopup')) {
19931 				enter({key: 'down'});
19932 			} else {
19933 				moveFocus(1);
19934 			}
19935 		}
19936 
19937 		/**
19938 		 * Moves the focus to the next item or previous item depending on shift key.
19939 		 *
19940 		 * @private
19941 		 * @param {DOMEvent} e DOM event object.
19942 		 */
19943 		function tab(e) {
19944 			var parentRole = getParentRole();
19945 
19946 			if (parentRole == "tablist") {
19947 				var elm = getFocusElements(focusedControl.getEl('body'))[0];
19948 
19949 				if (elm) {
19950 					elm.focus();
19951 				}
19952 			} else {
19953 				moveFocus(e.shiftKey ? -1 : 1);
19954 			}
19955 		}
19956 
19957 		/**
19958 		 * Calls the cancel event on the currently focused control. This is normally done using the Esc key.
19959 		 *
19960 		 * @private
19961 		 */
19962 		function cancel() {
19963 			focusedControl.fire('cancel');
19964 		}
19965 
19966 		/**
19967 		 * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys.
19968 		 *
19969 		 * @private
19970 		 * @param {Object} aria Optional aria data to pass along with the enter event.
19971 		 */
19972 		function enter(aria) {
19973 			aria = aria || {};
19974 			focusedControl.fire('click', {target: focusedElement, aria: aria});
19975 		}
19976 
19977 		root.on('keydown', function(e) {
19978 			function handleNonTabOrEscEvent(e, handler) {
19979 				// Ignore non tab keys for text elements
19980 				if (isTextInputElement(focusedElement)) {
19981 					return;
19982 				}
19983 
19984 				if (handler(e) !== false) {
19985 					e.preventDefault();
19986 				}
19987 			}
19988 
19989 			if (e.isDefaultPrevented()) {
19990 				return;
19991 			}
19992 
19993 			switch (e.keyCode) {
19994 				case 37: // DOM_VK_LEFT
19995 					handleNonTabOrEscEvent(e, left);
19996 					break;
19997 
19998 				case 39: // DOM_VK_RIGHT
19999 					handleNonTabOrEscEvent(e, right);
20000 					break;
20001 
20002 				case 38: // DOM_VK_UP
20003 					handleNonTabOrEscEvent(e, up);
20004 					break;
20005 
20006 				case 40: // DOM_VK_DOWN
20007 					handleNonTabOrEscEvent(e, down);
20008 					break;
20009 
20010 				case 27: // DOM_VK_ESCAPE
20011 					cancel();
20012 					break;
20013 
20014 				case 14: // DOM_VK_ENTER
20015 				case 13: // DOM_VK_RETURN
20016 				case 32: // DOM_VK_SPACE
20017 					handleNonTabOrEscEvent(e, enter);
20018 					break;
20019 
20020 				case 9: // DOM_VK_TAB
20021 					if (tab(e) !== false) {
20022 						e.preventDefault();
20023 					}
20024 					break;
20025 			}
20026 		});
20027 
20028 		root.on('focusin', function(e) {
20029 			focusedElement = e.target;
20030 			focusedControl = e.control;
20031 		});
20032 
20033 		return {
20034 			focusFirst: focusFirst
20035 		};
20036 	};
20037 });
20038 
20039 // Included from: js/tinymce/classes/ui/Container.js
20040 
20041 /**
20042  * Container.js
20043  *
20044  * Copyright, Moxiecode Systems AB
20045  * Released under LGPL License.
20046  *
20047  * License: http://www.tinymce.com/license
20048  * Contributing: http://www.tinymce.com/contributing
20049  */
20050 
20051 /**
20052  * Container control. This is extended by all controls that can have
20053  * children such as panels etc. You can also use this class directly as an
20054  * generic container instance. The container doesn't have any specific role or style.
20055  *
20056  * @-x-less Container.less
20057  * @class tinymce.ui.Container
20058  * @extends tinymce.ui.Control
20059  */
20060 define("tinymce/ui/Container", [
20061 	"tinymce/ui/Control",
20062 	"tinymce/ui/Collection",
20063 	"tinymce/ui/Selector",
20064 	"tinymce/ui/Factory",
20065 	"tinymce/ui/KeyboardNavigation",
20066 	"tinymce/util/Tools",
20067 	"tinymce/ui/DomUtils"
20068 ], function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, DomUtils) {
20069 	"use strict";
20070 
20071 	var selectorCache = {};
20072 
20073 	return Control.extend({
20074 		layout: '',
20075 		innerClass: 'container-inner',
20076 
20077 		/**
20078 		 * Constructs a new control instance with the specified settings.
20079 		 *
20080 		 * @constructor
20081 		 * @param {Object} settings Name/value object with settings.
20082 		 * @setting {Array} items Items to add to container in JSON format or control instances.
20083 		 * @setting {String} layout Layout manager by name to use.
20084 		 * @setting {Object} defaults Default settings to apply to all items.
20085 		 */
20086 		init: function(settings) {
20087 			var self = this;
20088 
20089 			self._super(settings);
20090 			settings = self.settings;
20091 			self._fixed = settings.fixed;
20092 			self._items = new Collection();
20093 
20094 			if (self.isRtl()) {
20095 				self.addClass('rtl');
20096 			}
20097 
20098 			self.addClass('container');
20099 			self.addClass('container-body', 'body');
20100 
20101 			if (settings.containerCls) {
20102 				self.addClass(settings.containerCls);
20103 			}
20104 
20105 			self._layout = Factory.create((settings.layout || self.layout) + 'layout');
20106 
20107 			if (self.settings.items) {
20108 				self.add(self.settings.items);
20109 			}
20110 
20111 			// TODO: Fix this!
20112 			self._hasBody = true;
20113 		},
20114 
20115 		/**
20116 		 * Returns a collection of child items that the container currently have.
20117 		 *
20118 		 * @method items
20119 		 * @return {tinymce.ui.Collection} Control collection direct child controls.
20120 		 */
20121 		items: function() {
20122 			return this._items;
20123 		},
20124 
20125 		/**
20126 		 * Find child controls by selector.
20127 		 *
20128 		 * @method find
20129 		 * @param {String} selector Selector CSS pattern to find children by.
20130 		 * @return {tinymce.ui.Collection} Control collection with child controls.
20131 		 */
20132 		find: function(selector) {
20133 			selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector);
20134 
20135 			return selector.find(this);
20136 		},
20137 
20138 		/**
20139 		 * Adds one or many items to the current container. This will create instances of
20140 		 * the object representations if needed.
20141 		 *
20142 		 * @method add
20143 		 * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container.
20144 		 * @return {tinymce.ui.Collection} Current collection control.
20145 		 */
20146 		add: function(items) {
20147 			var self = this;
20148 
20149 			self.items().add(self.create(items)).parent(self);
20150 
20151 			return self;
20152 		},
20153 
20154 		/**
20155 		 * Focuses the current container instance. This will look
20156 		 * for the first control in the container and focus that.
20157 		 *
20158 		 * @method focus
20159 		 * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not.
20160 		 * @return {tinymce.ui.Collection} Current instance.
20161 		 */
20162 		focus: function(keyboard) {
20163 			var self = this, focusCtrl, keyboardNav, items;
20164 
20165 			if (keyboard) {
20166 				keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav;
20167 
20168 				if (keyboardNav) {
20169 					keyboardNav.focusFirst(self);
20170 					return;
20171 				}
20172 			}
20173 
20174 			items = self.find('*');
20175 
20176 			// TODO: Figure out a better way to auto focus alert dialog buttons
20177 			if (self.statusbar) {
20178 				items.add(self.statusbar.items());
20179 			}
20180 
20181 			items.each(function(ctrl) {
20182 				if (ctrl.settings.autofocus) {
20183 					focusCtrl = null;
20184 					return false;
20185 				}
20186 
20187 				if (ctrl.canFocus) {
20188 					focusCtrl = focusCtrl || ctrl;
20189 				}
20190 			});
20191 
20192 			if (focusCtrl) {
20193 				focusCtrl.focus();
20194 			}
20195 
20196 			return self;
20197 		},
20198 
20199 		/**
20200 		 * Replaces the specified child control with a new control.
20201 		 *
20202 		 * @method replace
20203 		 * @param {tinymce.ui.Control} oldItem Old item to be replaced.
20204 		 * @param {tinymce.ui.Control} newItem New item to be inserted.
20205 		 */
20206 		replace: function(oldItem, newItem) {
20207 			var ctrlElm, items = this.items(), i = items.length;
20208 
20209 			// Replace the item in collection
20210 			while (i--) {
20211 				if (items[i] === oldItem) {
20212 					items[i] = newItem;
20213 					break;
20214 				}
20215 			}
20216 
20217 			if (i >= 0) {
20218 				// Remove new item from DOM
20219 				ctrlElm = newItem.getEl();
20220 				if (ctrlElm) {
20221 					ctrlElm.parentNode.removeChild(ctrlElm);
20222 				}
20223 
20224 				// Remove old item from DOM
20225 				ctrlElm = oldItem.getEl();
20226 				if (ctrlElm) {
20227 					ctrlElm.parentNode.removeChild(ctrlElm);
20228 				}
20229 			}
20230 
20231 			// Adopt the item
20232 			newItem.parent(this);
20233 		},
20234 
20235 		/**
20236 		 * Creates the specified items. If any of the items is plain JSON style objects
20237 		 * it will convert these into real tinymce.ui.Control instances.
20238 		 *
20239 		 * @method create
20240 		 * @param {Array} items Array of items to convert into control instances.
20241 		 * @return {Array} Array with control instances.
20242 		 */
20243 		create: function(items) {
20244 			var self = this, settings, ctrlItems = [];
20245 
20246 			// Non array structure, then force it into an array
20247 			if (!Tools.isArray(items)) {
20248 				items = [items];
20249 			}
20250 
20251 			// Add default type to each child control
20252 			Tools.each(items, function(item) {
20253 				if (item) {
20254 					// Construct item if needed
20255 					if (!(item instanceof Control)) {
20256 						// Name only then convert it to an object
20257 						if (typeof(item) == "string") {
20258 							item = {type: item};
20259 						}
20260 
20261 						// Create control instance based on input settings and default settings
20262 						settings = Tools.extend({}, self.settings.defaults, item);
20263 						item.type = settings.type = settings.type || item.type || self.settings.defaultType ||
20264 							(settings.defaults ? settings.defaults.type : null);
20265 						item = Factory.create(settings);
20266 					}
20267 
20268 					ctrlItems.push(item);
20269 				}
20270 			});
20271 
20272 			return ctrlItems;
20273 		},
20274 
20275 		/**
20276 		 * Renders new control instances.
20277 		 *
20278 		 * @private
20279 		 */
20280 		renderNew: function() {
20281 			var self = this;
20282 
20283 			// Render any new items
20284 			self.items().each(function(ctrl, index) {
20285 				var containerElm, fragment;
20286 
20287 				ctrl.parent(self);
20288 
20289 				if (!ctrl._rendered) {
20290 					containerElm = self.getEl('body');
20291 					fragment = DomUtils.createFragment(ctrl.renderHtml());
20292 
20293 					// Insert or append the item
20294 					if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) {
20295 						containerElm.insertBefore(fragment, containerElm.childNodes[index]);
20296 					} else {
20297 						containerElm.appendChild(fragment);
20298 					}
20299 
20300 					ctrl.postRender();
20301 				}
20302 			});
20303 
20304 			self._layout.applyClasses(self);
20305 			self._lastRect = null;
20306 
20307 			return self;
20308 		},
20309 
20310 		/**
20311 		 * Appends new instances to the current container.
20312 		 *
20313 		 * @method append
20314 		 * @param {Array/tinymce.ui.Collection} items Array if controls to append.
20315 		 * @return {tinymce.ui.Container} Current container instance.
20316 		 */
20317 		append: function(items) {
20318 			return this.add(items).renderNew();
20319 		},
20320 
20321 		/**
20322 		 * Prepends new instances to the current container.
20323 		 *
20324 		 * @method prepend
20325 		 * @param {Array/tinymce.ui.Collection} items Array if controls to prepend.
20326 		 * @return {tinymce.ui.Container} Current container instance.
20327 		 */
20328 		prepend: function(items) {
20329 			var self = this;
20330 
20331 			self.items().set(self.create(items).concat(self.items().toArray()));
20332 
20333 			return self.renderNew();
20334 		},
20335 
20336 		/**
20337 		 * Inserts an control at a specific index.
20338 		 *
20339 		 * @method insert
20340 		 * @param {Array/tinymce.ui.Collection} items Array if controls to insert.
20341 		 * @param {Number} index Index to insert controls at.
20342 		 * @param {Boolean} [before=false] Inserts controls before the index.
20343 		 */
20344 		insert: function(items, index, before) {
20345 			var self = this, curItems, beforeItems, afterItems;
20346 
20347 			items = self.create(items);
20348 			curItems = self.items();
20349 
20350 			if (!before && index < curItems.length - 1) {
20351 				index += 1;
20352 			}
20353 
20354 			if (index >= 0 && index < curItems.length) {
20355 				beforeItems = curItems.slice(0, index).toArray();
20356 				afterItems = curItems.slice(index).toArray();
20357 				curItems.set(beforeItems.concat(items, afterItems));
20358 			}
20359 
20360 			return self.renderNew();
20361 		},
20362 
20363 		/**
20364 		 * Populates the form fields from the specified JSON data object.
20365 		 *
20366 		 * Control items in the form that matches the data will have it's value set.
20367 		 *
20368 		 * @method fromJSON
20369 		 * @param {Object} data JSON data object to set control values by.
20370 		 * @return {tinymce.ui.Container} Current form instance.
20371 		 */
20372 		fromJSON: function(data) {
20373 			var self = this;
20374 
20375 			for (var name in data) {
20376 				self.find('#' + name).value(data[name]);
20377 			}
20378 
20379 			return self;
20380 		},
20381 
20382 		/**
20383 		 * Serializes the form into a JSON object by getting all items
20384 		 * that has a name and a value.
20385 		 *
20386 		 * @method toJSON
20387 		 * @return {Object} JSON object with form data.
20388 		 */
20389 		toJSON: function() {
20390 			var self = this, data = {};
20391 
20392 			self.find('*').each(function(ctrl) {
20393 				var name = ctrl.name(), value = ctrl.value();
20394 
20395 				if (name && typeof(value) != "undefined") {
20396 					data[name] = value;
20397 				}
20398 			});
20399 
20400 			return data;
20401 		},
20402 
20403 		preRender: function() {
20404 		},
20405 
20406 		/**
20407 		 * Renders the control as a HTML string.
20408 		 *
20409 		 * @method renderHtml
20410 		 * @return {String} HTML representing the control.
20411 		 */
20412 		renderHtml: function() {
20413 			var self = this, layout = self._layout, role = this.settings.role;
20414 
20415 			self.preRender();
20416 			layout.preRender(self);
20417 
20418 			return (
20419 				'<div id="' + self._id + '" class="' + self.classes() + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>' +
20420 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
20421 						(self.settings.html || '') + layout.renderHtml(self) +
20422 					'</div>' +
20423 				'</div>'
20424 			);
20425 		},
20426 
20427 		/**
20428 		 * Post render method. Called after the control has been rendered to the target.
20429 		 *
20430 		 * @method postRender
20431 		 * @return {tinymce.ui.Container} Current combobox instance.
20432 		 */
20433 		postRender: function() {
20434 			var self = this, box;
20435 
20436 			self.items().exec('postRender');
20437 			self._super();
20438 
20439 			self._layout.postRender(self);
20440 			self._rendered = true;
20441 
20442 			if (self.settings.style) {
20443 				DomUtils.css(self.getEl(), self.settings.style);
20444 			}
20445 
20446 			if (self.settings.border) {
20447 				box = self.borderBox();
20448 				DomUtils.css(self.getEl(), {
20449 					'border-top-width': box.top,
20450 					'border-right-width': box.right,
20451 					'border-bottom-width': box.bottom,
20452 					'border-left-width': box.left
20453 				});
20454 			}
20455 
20456 			if (!self.parent()) {
20457 				self.keyboardNav = new KeyboardNavigation({
20458 					root: self
20459 				});
20460 			}
20461 
20462 			return self;
20463 		},
20464 
20465 		/**
20466 		 * Initializes the current controls layout rect.
20467 		 * This will be executed by the layout managers to determine the
20468 		 * default minWidth/minHeight etc.
20469 		 *
20470 		 * @method initLayoutRect
20471 		 * @return {Object} Layout rect instance.
20472 		 */
20473 		initLayoutRect: function() {
20474 			var self = this, layoutRect = self._super();
20475 
20476 			// Recalc container size by asking layout manager
20477 			self._layout.recalc(self);
20478 
20479 			return layoutRect;
20480 		},
20481 
20482 		/**
20483 		 * Recalculates the positions of the controls in the current container.
20484 		 * This is invoked by the reflow method and shouldn't be called directly.
20485 		 *
20486 		 * @method recalc
20487 		 */
20488 		recalc: function() {
20489 			var self = this, rect = self._layoutRect, lastRect = self._lastRect;
20490 
20491 			if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) {
20492 				self._layout.recalc(self);
20493 				rect = self.layoutRect();
20494 				self._lastRect = {x: rect.x, y: rect.y, w: rect.w, h: rect.h};
20495 				return true;
20496 			}
20497 		},
20498 
20499 		/**
20500 		 * Reflows the current container and it's children and possible parents.
20501 		 * This should be used after you for example append children to the current control so
20502 		 * that the layout managers know that they need to reposition everything.
20503 		 *
20504 		 * @example
20505 		 * container.append({type: 'button', text: 'My button'}).reflow();
20506 		 *
20507 		 * @method reflow
20508 		 * @return {tinymce.ui.Container} Current container instance.
20509 		 */
20510 		reflow: function() {
20511 			var i;
20512 
20513 			if (this.visible()) {
20514 				Control.repaintControls = [];
20515 				Control.repaintControls.map = {};
20516 
20517 				this.recalc();
20518 				i = Control.repaintControls.length;
20519 
20520 				while (i--) {
20521 					Control.repaintControls[i].repaint();
20522 				}
20523 
20524 				// TODO: Fix me!
20525 				if (this.settings.layout !== "flow" && this.settings.layout !== "stack") {
20526 					this.repaint();
20527 				}
20528 
20529 				Control.repaintControls = [];
20530 			}
20531 
20532 			return this;
20533 		}
20534 	});
20535 });
20536 
20537 // Included from: js/tinymce/classes/ui/DragHelper.js
20538 
20539 /**
20540  * DragHelper.js
20541  *
20542  * Copyright, Moxiecode Systems AB
20543  * Released under LGPL License.
20544  *
20545  * License: http://www.tinymce.com/license
20546  * Contributing: http://www.tinymce.com/contributing
20547  */
20548 
20549 /**
20550  * Drag/drop helper class.
20551  *
20552  * @example
20553  * var dragHelper = new tinymce.ui.DragHelper('mydiv', {
20554  *     start: function(evt) {
20555  *     },
20556  *
20557  *     drag: function(evt) {
20558  *     },
20559  *
20560  *     end: function(evt) {
20561  *     }
20562  * });
20563  *
20564  * @class tinymce.ui.DragHelper
20565  */
20566 define("tinymce/ui/DragHelper", [
20567 	"tinymce/ui/DomUtils"
20568 ], function(DomUtils) {
20569 	"use strict";
20570 
20571 	function getDocumentSize() {
20572 		var doc = document, documentElement, body, scrollWidth, clientWidth;
20573 		var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max;
20574 
20575 		documentElement = doc.documentElement;
20576 		body = doc.body;
20577 
20578 		scrollWidth = max(documentElement.scrollWidth, body.scrollWidth);
20579 		clientWidth = max(documentElement.clientWidth, body.clientWidth);
20580 		offsetWidth = max(documentElement.offsetWidth, body.offsetWidth);
20581 
20582 		scrollHeight = max(documentElement.scrollHeight, body.scrollHeight);
20583 		clientHeight = max(documentElement.clientHeight, body.clientHeight);
20584 		offsetHeight = max(documentElement.offsetHeight, body.offsetHeight);
20585 
20586 		return {
20587 			width: scrollWidth < offsetWidth ? clientWidth : scrollWidth,
20588 			height: scrollHeight < offsetHeight ? clientHeight : scrollHeight
20589 		};
20590 	}
20591 
20592 	return function(id, settings) {
20593 		var eventOverlayElm, doc = document, downButton, start, stop, drag, startX, startY;
20594 
20595 		settings = settings || {};
20596 
20597 		function getHandleElm() {
20598 			return doc.getElementById(settings.handle || id);
20599 		}
20600 
20601 		start = function(e) {
20602 			var docSize = getDocumentSize(), handleElm, cursor;
20603 
20604 			e.preventDefault();
20605 			downButton = e.button;
20606 			handleElm = getHandleElm();
20607 			startX = e.screenX;
20608 			startY = e.screenY;
20609 
20610 			// Grab cursor from handle
20611 			if (window.getComputedStyle) {
20612 				cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor");
20613 			} else {
20614 				cursor = handleElm.runtimeStyle.cursor;
20615 			}
20616 
20617 			// Create event overlay and add it to document
20618 			eventOverlayElm = doc.createElement('div');
20619 			DomUtils.css(eventOverlayElm, {
20620 				position: "absolute",
20621 				top: 0, left: 0,
20622 				width: docSize.width,
20623 				height: docSize.height,
20624 				zIndex: 0x7FFFFFFF,
20625 				opacity: 0.0001,
20626 				background: 'red',
20627 				cursor: cursor
20628 			});
20629 
20630 			doc.body.appendChild(eventOverlayElm);
20631 
20632 			// Bind mouse events
20633 			DomUtils.on(doc, 'mousemove', drag);
20634 			DomUtils.on(doc, 'mouseup', stop);
20635 
20636 			// Begin drag
20637 			settings.start(e);
20638 		};
20639 
20640 		drag = function(e) {
20641 			if (e.button !== downButton) {
20642 				return stop(e);
20643 			}
20644 
20645 			e.deltaX = e.screenX - startX;
20646 			e.deltaY = e.screenY - startY;
20647 
20648 			e.preventDefault();
20649 			settings.drag(e);
20650 		};
20651 
20652 		stop = function(e) {
20653 			DomUtils.off(doc, 'mousemove', drag);
20654 			DomUtils.off(doc, 'mouseup', stop);
20655 
20656 			eventOverlayElm.parentNode.removeChild(eventOverlayElm);
20657 
20658 			if (settings.stop) {
20659 				settings.stop(e);
20660 			}
20661 		};
20662 
20663 		/**
20664 		 * Destroys the drag/drop helper instance.
20665 		 *
20666 		 * @method destroy
20667 		 */
20668 		this.destroy = function() {
20669 			DomUtils.off(getHandleElm());
20670 		};
20671 
20672 		DomUtils.on(getHandleElm(), 'mousedown', start);
20673 	};
20674 });
20675 
20676 // Included from: js/tinymce/classes/ui/Scrollable.js
20677 
20678 /**
20679  * Scrollable.js
20680  *
20681  * Copyright, Moxiecode Systems AB
20682  * Released under LGPL License.
20683  *
20684  * License: http://www.tinymce.com/license
20685  * Contributing: http://www.tinymce.com/contributing
20686  */
20687 
20688 /**
20689  * This mixin makes controls scrollable using custom scrollbars.
20690  *
20691  * @-x-less Scrollable.less
20692  * @mixin tinymce.ui.Scrollable
20693  */
20694 define("tinymce/ui/Scrollable", [
20695 	"tinymce/ui/DomUtils",
20696 	"tinymce/ui/DragHelper"
20697 ], function(DomUtils, DragHelper) {
20698 	"use strict";
20699 
20700 	return {
20701 		init: function() {
20702 			var self = this;
20703 			self.on('repaint', self.renderScroll);
20704 		},
20705 
20706 		renderScroll: function() {
20707 			var self = this, margin = 2;
20708 
20709 			function repaintScroll() {
20710 				var hasScrollH, hasScrollV, bodyElm;
20711 
20712 				function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) {
20713 					var containerElm, scrollBarElm, scrollThumbElm;
20714 					var containerSize, scrollSize, ratio, rect;
20715 					var posNameLower, sizeNameLower;
20716 
20717 					scrollBarElm = self.getEl('scroll' + axisName);
20718 					if (scrollBarElm) {
20719 						posNameLower = posName.toLowerCase();
20720 						sizeNameLower = sizeName.toLowerCase();
20721 
20722 						if (self.getEl('absend')) {
20723 							DomUtils.css(self.getEl('absend'), posNameLower, self.layoutRect()[contentSizeName] - 1);
20724 						}
20725 
20726 						if (!hasScroll) {
20727 							DomUtils.css(scrollBarElm, 'display', 'none');
20728 							return;
20729 						}
20730 
20731 						DomUtils.css(scrollBarElm, 'display', 'block');
20732 						containerElm = self.getEl('body');
20733 						scrollThumbElm = self.getEl('scroll' + axisName + "t");
20734 						containerSize = containerElm["client" + sizeName] - (margin * 2);
20735 						containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0;
20736 						scrollSize = containerElm["scroll" + sizeName];
20737 						ratio = containerSize / scrollSize;
20738 
20739 						rect = {};
20740 						rect[posNameLower] = containerElm["offset" + posName] + margin;
20741 						rect[sizeNameLower] = containerSize;
20742 						DomUtils.css(scrollBarElm, rect);
20743 
20744 						rect = {};
20745 						rect[posNameLower] = containerElm["scroll" + posName] * ratio;
20746 						rect[sizeNameLower] = containerSize * ratio;
20747 						DomUtils.css(scrollThumbElm, rect);
20748 					}
20749 				}
20750 
20751 				bodyElm = self.getEl('body');
20752 				hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth;
20753 				hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight;
20754 
20755 				repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height");
20756 				repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width");
20757 			}
20758 
20759 			function addScroll() {
20760 				function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) {
20761 					var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix;
20762 
20763 					self.getEl().appendChild(DomUtils.createFragment(
20764 						'<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' +
20765 							'<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' +
20766 						'</div>'
20767 					));
20768 
20769 					self.draghelper = new DragHelper(axisId + 't', {
20770 						start: function() {
20771 							scrollStart = self.getEl('body')["scroll" + posName];
20772 							DomUtils.addClass(DomUtils.get(axisId), prefix + 'active');
20773 						},
20774 
20775 						drag: function(e) {
20776 							var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect();
20777 
20778 							hasScrollH = layoutRect.contentW > layoutRect.innerW;
20779 							hasScrollV = layoutRect.contentH > layoutRect.innerH;
20780 							containerSize = self.getEl('body')["client" + sizeName] - (margin * 2);
20781 							containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0;
20782 
20783 							ratio = containerSize / self.getEl('body')["scroll" + sizeName];
20784 							self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio);
20785 						},
20786 
20787 						stop: function() {
20788 							DomUtils.removeClass(DomUtils.get(axisId), prefix + 'active');
20789 						}
20790 					});
20791 /*
20792 					self.on('click', function(e) {
20793 						if (e.target.id == self._id + '-scrollv') {
20794 
20795 						}
20796 					});*/
20797 				}
20798 
20799 				self.addClass('scroll');
20800 
20801 				addScrollAxis("v", "Top", "Height", "Y", "Width");
20802 				addScrollAxis("h", "Left", "Width", "X", "Height");
20803 			}
20804 
20805 			if (self.settings.autoScroll) {
20806 				if (!self._hasScroll) {
20807 					self._hasScroll = true;
20808 					addScroll();
20809 
20810 					self.on('wheel', function(e) {
20811 						var bodyEl = self.getEl('body');
20812 
20813 						bodyEl.scrollLeft += (e.deltaX || 0) * 10;
20814 						bodyEl.scrollTop += e.deltaY * 10;
20815 
20816 						repaintScroll();
20817 					});
20818 
20819 					DomUtils.on(self.getEl('body'), "scroll", repaintScroll);
20820 				}
20821 
20822 				repaintScroll();
20823 			}
20824 		}
20825 	};
20826 });
20827 
20828 // Included from: js/tinymce/classes/ui/Panel.js
20829 
20830 /**
20831  * Panel.js
20832  *
20833  * Copyright, Moxiecode Systems AB
20834  * Released under LGPL License.
20835  *
20836  * License: http://www.tinymce.com/license
20837  * Contributing: http://www.tinymce.com/contributing
20838  */
20839 
20840 /**
20841  * Creates a new panel.
20842  *
20843  * @-x-less Panel.less
20844  * @class tinymce.ui.Panel
20845  * @extends tinymce.ui.Container
20846  * @mixes tinymce.ui.Scrollable
20847  */
20848 define("tinymce/ui/Panel", [
20849 	"tinymce/ui/Container",
20850 	"tinymce/ui/Scrollable"
20851 ], function(Container, Scrollable) {
20852 	"use strict";
20853 
20854 	return Container.extend({
20855 		Defaults: {
20856 			layout: 'fit',
20857 			containerCls: 'panel'
20858 		},
20859 
20860 		Mixins: [Scrollable],
20861 
20862 		/**
20863 		 * Renders the control as a HTML string.
20864 		 *
20865 		 * @method renderHtml
20866 		 * @return {String} HTML representing the control.
20867 		 */
20868 		renderHtml: function() {
20869 			var self = this, layout = self._layout, innerHtml = self.settings.html;
20870 
20871 			self.preRender();
20872 			layout.preRender(self);
20873 
20874 			if (typeof(innerHtml) == "undefined") {
20875 				innerHtml = (
20876 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
20877 						layout.renderHtml(self) +
20878 					'</div>'
20879 				);
20880 			} else {
20881 				if (typeof(innerHtml) == 'function') {
20882 					innerHtml = innerHtml.call(self);
20883 				}
20884 
20885 				self._hasBody = false;
20886 			}
20887 
20888 			return (
20889 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1" role="group">' +
20890 					(self._preBodyHtml || '') +
20891 					innerHtml +
20892 				'</div>'
20893 			);
20894 		}
20895 	});
20896 });
20897 
20898 // Included from: js/tinymce/classes/ui/Movable.js
20899 
20900 /**
20901  * Movable.js
20902  *
20903  * Copyright, Moxiecode Systems AB
20904  * Released under LGPL License.
20905  *
20906  * License: http://www.tinymce.com/license
20907  * Contributing: http://www.tinymce.com/contributing
20908  */
20909 
20910 /**
20911  * Movable mixin. Makes controls movable absolute and relative to other elements.
20912  *
20913  * @mixin tinymce.ui.Movable
20914  */
20915 define("tinymce/ui/Movable", [
20916 	"tinymce/ui/DomUtils"
20917 ], function(DomUtils) {
20918 	"use strict";
20919 
20920 	function calculateRelativePosition(ctrl, targetElm, rel) {
20921 		var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size;
20922 
20923 		viewport = DomUtils.getViewPort();
20924 
20925 		// Get pos of target
20926 		pos = DomUtils.getPos(targetElm);
20927 		x = pos.x;
20928 		y = pos.y;
20929 
20930 		if (ctrl._fixed) {
20931 			x -= viewport.x;
20932 			y -= viewport.y;
20933 		}
20934 
20935 		// Get size of self
20936 		ctrlElm = ctrl.getEl();
20937 		size = DomUtils.getSize(ctrlElm);
20938 		selfW = size.width;
20939 		selfH = size.height;
20940 
20941 		// Get size of target
20942 		size = DomUtils.getSize(targetElm);
20943 		targetW = size.width;
20944 		targetH = size.height;
20945 
20946 		// Parse align string
20947 		rel = (rel || '').split('');
20948 
20949 		// Target corners
20950 		if (rel[0] === 'b') {
20951 			y += targetH;
20952 		}
20953 
20954 		if (rel[1] === 'r') {
20955 			x += targetW;
20956 		}
20957 
20958 		if (rel[0] === 'c') {
20959 			y += Math.round(targetH / 2);
20960 		}
20961 
20962 		if (rel[1] === 'c') {
20963 			x += Math.round(targetW / 2);
20964 		}
20965 
20966 		// Self corners
20967 		if (rel[3] === 'b') {
20968 			y -= selfH;
20969 		}
20970 
20971 		if (rel[4] === 'r') {
20972 			x -= selfW;
20973 		}
20974 
20975 		if (rel[3] === 'c') {
20976 			y -= Math.round(selfH / 2);
20977 		}
20978 
20979 		if (rel[4] === 'c') {
20980 			x -= Math.round(selfW / 2);
20981 		}
20982 
20983 		return {
20984 			x: x,
20985 			y: y,
20986 			w: selfW,
20987 			h: selfH
20988 		};
20989 	}
20990 
20991 	return {
20992 		/**
20993 		 * Tests various positions to get the most suitable one.
20994 		 *
20995 		 * @method testMoveRel
20996 		 * @param {DOMElement} elm Element to position against.
20997 		 * @param {Array} rels Array with relative positions.
20998 		 * @return {String} Best suitable relative position.
20999 		 */
21000 		testMoveRel: function(elm, rels) {
21001 			var viewPortRect = DomUtils.getViewPort();
21002 
21003 			for (var i = 0; i < rels.length; i++) {
21004 				var pos = calculateRelativePosition(this, elm, rels[i]);
21005 
21006 				if (this._fixed) {
21007 					if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) {
21008 						return rels[i];
21009 					}
21010 				} else {
21011 					if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x &&
21012 						pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) {
21013 						return rels[i];
21014 					}
21015 				}
21016 			}
21017 
21018 			return rels[0];
21019 		},
21020 
21021 		/**
21022 		 * Move relative to the specified element.
21023 		 *
21024 		 * @method moveRel
21025 		 * @param {Element} elm Element to move relative to.
21026 		 * @param {String} rel Relative mode. For example: br-tl.
21027 		 * @return {tinymce.ui.Control} Current control instance.
21028 		 */
21029 		moveRel: function(elm, rel) {
21030 			if (typeof(rel) != 'string') {
21031 				rel = this.testMoveRel(elm, rel);
21032 			}
21033 
21034 			var pos = calculateRelativePosition(this, elm, rel);
21035 			return this.moveTo(pos.x, pos.y);
21036 		},
21037 
21038 		/**
21039 		 * Move by a relative x, y values.
21040 		 *
21041 		 * @method moveBy
21042 		 * @param {Number} dx Relative x position.
21043 		 * @param {Number} dy Relative y position.
21044 		 * @return {tinymce.ui.Control} Current control instance.
21045 		 */
21046 		moveBy: function(dx, dy) {
21047 			var self = this, rect = self.layoutRect();
21048 
21049 			self.moveTo(rect.x + dx, rect.y + dy);
21050 
21051 			return self;
21052 		},
21053 
21054 		/**
21055 		 * Move to absolute position.
21056 		 *
21057 		 * @method moveTo
21058 		 * @param {Number} x Absolute x position.
21059 		 * @param {Number} y Absolute y position.
21060 		 * @return {tinymce.ui.Control} Current control instance.
21061 		 */
21062 		moveTo: function(x, y) {
21063 			var self = this;
21064 
21065 			// TODO: Move this to some global class
21066 			function contrain(value, max, size) {
21067 				if (value < 0) {
21068 					return 0;
21069 				}
21070 
21071 				if (value + size > max) {
21072 					value = max - size;
21073 					return value < 0 ? 0 : value;
21074 				}
21075 
21076 				return value;
21077 			}
21078 
21079 			if (self.settings.constrainToViewport) {
21080 				var viewPortRect = DomUtils.getViewPort(window);
21081 				var layoutRect = self.layoutRect();
21082 
21083 				x = contrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w);
21084 				y = contrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h);
21085 			}
21086 
21087 			if (self._rendered) {
21088 				self.layoutRect({x: x, y: y}).repaint();
21089 			} else {
21090 				self.settings.x = x;
21091 				self.settings.y = y;
21092 			}
21093 
21094 			self.fire('move', {x: x, y: y});
21095 
21096 			return self;
21097 		}
21098 	};
21099 });
21100 
21101 // Included from: js/tinymce/classes/ui/Resizable.js
21102 
21103 /**
21104  * Resizable.js
21105  *
21106  * Copyright, Moxiecode Systems AB
21107  * Released under LGPL License.
21108  *
21109  * License: http://www.tinymce.com/license
21110  * Contributing: http://www.tinymce.com/contributing
21111  */
21112 
21113 /**
21114  * Resizable mixin. Enables controls to be resized.
21115  *
21116  * @mixin tinymce.ui.Resizable
21117  */
21118 define("tinymce/ui/Resizable", [
21119 	"tinymce/ui/DomUtils"
21120 ], function(DomUtils) {
21121 	"use strict";
21122 
21123 	return {
21124 		/** 
21125 		 * Resizes the control to contents.
21126 		 *
21127 		 * @method resizeToContent
21128 		 */
21129 		resizeToContent: function() {
21130 			this._layoutRect.autoResize = true;
21131 			this._lastRect = null;
21132 			this.reflow();
21133 		},
21134 
21135 		/** 
21136 		 * Resizes the control to a specific width/height.
21137 		 *
21138 		 * @method resizeTo
21139 		 * @param {Number} w Control width.
21140 		 * @param {Number} h Control height.
21141 		 * @return {tinymce.ui.Control} Current control instance.
21142 		 */
21143 		resizeTo: function(w, h) {
21144 			// TODO: Fix hack
21145 			if (w <= 1 || h <= 1) {
21146 				var rect = DomUtils.getWindowSize();
21147 
21148 				w = w <= 1 ? w * rect.w : w;
21149 				h = h <= 1 ? h * rect.h : h;
21150 			}
21151 
21152 			this._layoutRect.autoResize = false;
21153 			return this.layoutRect({minW: w, minH: h, w: w, h: h}).reflow();
21154 		},
21155 
21156 		/** 
21157 		 * Resizes the control to a specific relative width/height.
21158 		 *
21159 		 * @method resizeBy
21160 		 * @param {Number} dw Relative control width.
21161 		 * @param {Number} dh Relative control height.
21162 		 * @return {tinymce.ui.Control} Current control instance.
21163 		 */
21164 		resizeBy: function(dw, dh) {
21165 			var self = this, rect = self.layoutRect();
21166 
21167 			return self.resizeTo(rect.w + dw, rect.h + dh);
21168 		}
21169 	};
21170 });
21171 
21172 // Included from: js/tinymce/classes/ui/FloatPanel.js
21173 
21174 /**
21175  * FloatPanel.js
21176  *
21177  * Copyright, Moxiecode Systems AB
21178  * Released under LGPL License.
21179  *
21180  * License: http://www.tinymce.com/license
21181  * Contributing: http://www.tinymce.com/contributing
21182  */
21183 
21184 /**
21185  * This class creates a floating panel.
21186  *
21187  * @-x-less FloatPanel.less
21188  * @class tinymce.ui.FloatPanel
21189  * @extends tinymce.ui.Panel
21190  * @mixes tinymce.ui.Movable
21191  * @mixes tinymce.ui.Resizable
21192  */
21193 define("tinymce/ui/FloatPanel", [
21194 	"tinymce/ui/Panel",
21195 	"tinymce/ui/Movable",
21196 	"tinymce/ui/Resizable",
21197 	"tinymce/ui/DomUtils"
21198 ], function(Panel, Movable, Resizable, DomUtils) {
21199 	"use strict";
21200 
21201 	var documentClickHandler, documentScrollHandler, visiblePanels = [];
21202 	var zOrder = [], hasModal;
21203 
21204 	var FloatPanel = Panel.extend({
21205 		Mixins: [Movable, Resizable],
21206 
21207 		/**
21208 		 * Constructs a new control instance with the specified settings.
21209 		 *
21210 		 * @constructor
21211 		 * @param {Object} settings Name/value object with settings.
21212 		 * @setting {Boolean} autohide Automatically hide the panel.
21213 		 */
21214 		init: function(settings) {
21215 			var self = this;
21216 
21217 			function reorder() {
21218 				var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal;
21219 
21220 				if (zOrder.length) {
21221 					for (i = 0; i < zOrder.length; i++) {
21222 						if (zOrder[i].modal) {
21223 							zIndex++;
21224 							topModal = zOrder[i];
21225 						}
21226 
21227 						zOrder[i].getEl().style.zIndex = zIndex;
21228 						zOrder[i].zIndex = zIndex;
21229 						zIndex++;
21230 					}
21231 				}
21232 
21233 				var modalBlockEl = document.getElementById(self.classPrefix + 'modal-block');
21234 
21235 				if (topModal) {
21236 					DomUtils.css(modalBlockEl, 'z-index', topModal.zIndex - 1);
21237 				} else if (modalBlockEl) {
21238 					modalBlockEl.parentNode.removeChild(modalBlockEl);
21239 					hasModal = false;
21240 				}
21241 
21242 				FloatPanel.currentZIndex = zIndex;
21243 			}
21244 
21245 			function isChildOf(ctrl, parent) {
21246 				while (ctrl) {
21247 					if (ctrl == parent) {
21248 						return true;
21249 					}
21250 
21251 					ctrl = ctrl.parent();
21252 				}
21253 			}
21254 
21255 			/**
21256 			 * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
21257 			 * also reposition all child panels of the current panel.
21258 			 */
21259 			function repositionPanel(panel) {
21260 				var scrollY = DomUtils.getViewPort().y;
21261 
21262 				function toggleFixedChildPanels(fixed, deltaY) {
21263 					var parent;
21264 
21265 					for (var i = 0; i < visiblePanels.length; i++) {
21266 						if (visiblePanels[i] != panel) {
21267 							parent = visiblePanels[i].parent();
21268 
21269 							while (parent && (parent = parent.parent())) {
21270 								if (parent == panel) {
21271 									visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
21272 								}
21273 							}
21274 						}
21275 					}
21276 				}
21277 
21278 				if (panel.settings.autofix) {
21279 					if (!panel._fixed) {
21280 						panel._autoFixY = panel.layoutRect().y;
21281 
21282 						if (panel._autoFixY < scrollY) {
21283 							panel.fixed(true).layoutRect({y: 0}).repaint();
21284 							toggleFixedChildPanels(true, scrollY - panel._autoFixY);
21285 						}
21286 					} else {
21287 						if (panel._autoFixY > scrollY) {
21288 							panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
21289 							toggleFixedChildPanels(false, panel._autoFixY - scrollY);
21290 						}
21291 					}
21292 				}
21293 			}
21294 
21295 			self._super(settings);
21296 			self._eventsRoot = self;
21297 
21298 			self.addClass('floatpanel');
21299 
21300 			// Hide floatpanes on click out side the root button
21301 			if (settings.autohide) {
21302 				if (!documentClickHandler) {
21303 					documentClickHandler = function(e) {
21304 						// Hide any float panel when a click is out side that float panel and the
21305 						// float panels direct parent for example a click on a menu button
21306 						var i = visiblePanels.length;
21307 						while (i--) {
21308 							var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
21309 
21310 							if (panel.settings.autohide) {
21311 								if (clickCtrl) {
21312 									if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
21313 										continue;
21314 									}
21315 								}
21316 
21317 								e = panel.fire('autohide', {target: e.target});
21318 								if (!e.isDefaultPrevented()) {
21319 									panel.hide();
21320 								}
21321 							}
21322 						}
21323 					};
21324 
21325 					DomUtils.on(document, 'click', documentClickHandler);
21326 				}
21327 
21328 				visiblePanels.push(self);
21329 			}
21330 
21331 			if (settings.autofix) {
21332 				if (!documentScrollHandler) {
21333 					documentScrollHandler = function() {
21334 						var i;
21335 
21336 						i = visiblePanels.length;
21337 						while (i--) {
21338 							repositionPanel(visiblePanels[i]);
21339 						}
21340 					};
21341 
21342 					DomUtils.on(window, 'scroll', documentScrollHandler);
21343 				}
21344 
21345 				self.on('move', function() {
21346 					repositionPanel(this);
21347 				});
21348 			}
21349 
21350 			self.on('postrender show', function(e) {
21351 				if (e.control == self) {
21352 					var modalBlockEl, prefix = self.classPrefix;
21353 
21354 					if (self.modal && !hasModal) {
21355 						modalBlockEl = DomUtils.createFragment('<div id="' + prefix + 'modal-block" class="' +
21356 							prefix + 'reset ' + prefix + 'fade"></div>');
21357 						modalBlockEl = modalBlockEl.firstChild;
21358 
21359 						self.getContainerElm().appendChild(modalBlockEl);
21360 
21361 						setTimeout(function() {
21362 							DomUtils.addClass(modalBlockEl, prefix + 'in');
21363 							DomUtils.addClass(self.getEl(), prefix + 'in');
21364 						}, 0);
21365 
21366 						hasModal = true;
21367 					}
21368 
21369 					zOrder.push(self);
21370 					reorder();
21371 				}
21372 			});
21373 
21374 			self.on('close hide', function(e) {
21375 				if (e.control == self) {
21376 					var i = zOrder.length;
21377 
21378 					while (i--) {
21379 						if (zOrder[i] === self) {
21380 							zOrder.splice(i, 1);
21381 						}
21382 					}
21383 
21384 					reorder();
21385 				}
21386 			});
21387 
21388 			self.on('show', function() {
21389 				self.parents().each(function(ctrl) {
21390 					if (ctrl._fixed) {
21391 						self.fixed(true);
21392 						return false;
21393 					}
21394 				});
21395 			});
21396 
21397 			if (settings.popover) {
21398 				self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>';
21399 				self.addClass('popover').addClass('bottom').addClass(self.isRtl() ? 'end' : 'start');
21400 			}
21401 		},
21402 
21403 		fixed: function(state) {
21404 			var self = this;
21405 
21406 			if (self._fixed != state) {
21407 				if (self._rendered) {
21408 					var viewport = DomUtils.getViewPort();
21409 
21410 					if (state) {
21411 						self.layoutRect().y -= viewport.y;
21412 					} else {
21413 						self.layoutRect().y += viewport.y;
21414 					}
21415 				}
21416 
21417 				self.toggleClass('fixed', state);
21418 				self._fixed = state;
21419 			}
21420 
21421 			return self;
21422 		},
21423 
21424 		/**
21425 		 * Shows the current float panel.
21426 		 *
21427 		 * @method show
21428 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
21429 		 */
21430 		show: function() {
21431 			var self = this, i, state = self._super();
21432 
21433 			i = visiblePanels.length;
21434 			while (i--) {
21435 				if (visiblePanels[i] === self) {
21436 					break;
21437 				}
21438 			}
21439 
21440 			if (i === -1) {
21441 				visiblePanels.push(self);
21442 			}
21443 
21444 			return state;
21445 		},
21446 
21447 		/**
21448 		 * Hides the current float panel.
21449 		 *
21450 		 * @method hide
21451 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
21452 		 */
21453 		hide: function() {
21454 			removeVisiblePanel(this);
21455 			return this._super();
21456 		},
21457 
21458 		/**
21459 		 * Hides all visible the float panels.
21460 		 *
21461 		 * @method hideAll
21462 		 */
21463 		hideAll: function() {
21464 			FloatPanel.hideAll();
21465 		},
21466 
21467 		/**
21468 		 * Closes the float panel. This will remove the float panel from page and fire the close event.
21469 		 *
21470 		 * @method close
21471 		 */
21472 		close: function() {
21473 			var self = this;
21474 
21475 			self.fire('close');
21476 
21477 			return self.remove();
21478 		},
21479 
21480 		/**
21481 		 * Removes the float panel from page.
21482 		 *
21483 		 * @method remove
21484 		 */
21485 		remove: function() {
21486 			removeVisiblePanel(this);
21487 			this._super();
21488 		},
21489 
21490 		postRender: function() {
21491 			var self = this;
21492 
21493 			if (self.settings.bodyRole) {
21494 				this.getEl('body').setAttribute('role', self.settings.bodyRole);
21495 			}
21496 
21497 			return self._super();
21498 		}
21499 	});
21500 
21501 	/**
21502 	 * Hides all visible the float panels.
21503 	 *
21504 	 * @static
21505 	 * @method hideAll
21506 	 */
21507 	FloatPanel.hideAll = function() {
21508 		var i = visiblePanels.length;
21509 
21510 		while (i--) {
21511 			var panel = visiblePanels[i];
21512 
21513 			if (panel && panel.settings.autohide) {
21514 				panel.hide();
21515 				visiblePanels.splice(i, 1);
21516 			}
21517 		}
21518 	};
21519 
21520 	function removeVisiblePanel(panel) {
21521 		var i;
21522 
21523 		i = visiblePanels.length;
21524 		while (i--) {
21525 			if (visiblePanels[i] === panel) {
21526 				visiblePanels.splice(i, 1);
21527 			}
21528 		}
21529 
21530 		i = zOrder.length;
21531 		while (i--) {
21532 			if (zOrder[i] === panel) {
21533 				zOrder.splice(i, 1);
21534 			}
21535 		}
21536 	}
21537 
21538 	return FloatPanel;
21539 });
21540 
21541 // Included from: js/tinymce/classes/ui/Window.js
21542 
21543 /**
21544  * Window.js
21545  *
21546  * Copyright, Moxiecode Systems AB
21547  * Released under LGPL License.
21548  *
21549  * License: http://www.tinymce.com/license
21550  * Contributing: http://www.tinymce.com/contributing
21551  */
21552 
21553 /**
21554  * Creates a new window.
21555  *
21556  * @-x-less Window.less
21557  * @class tinymce.ui.Window
21558  * @extends tinymce.ui.FloatPanel
21559  */
21560 define("tinymce/ui/Window", [
21561 	"tinymce/ui/FloatPanel",
21562 	"tinymce/ui/Panel",
21563 	"tinymce/ui/DomUtils",
21564 	"tinymce/ui/DragHelper"
21565 ], function(FloatPanel, Panel, DomUtils, DragHelper) {
21566 	"use strict";
21567 
21568 	var Window = FloatPanel.extend({
21569 		modal: true,
21570 
21571 		Defaults: {
21572 			border: 1,
21573 			layout: 'flex',
21574 			containerCls: 'panel',
21575 			role: 'dialog',
21576 			callbacks: {
21577 				submit: function() {
21578 					this.fire('submit', {data: this.toJSON()});
21579 				},
21580 
21581 				close: function() {
21582 					this.close();
21583 				}
21584 			}
21585 		},
21586 
21587 		/**
21588 		 * Constructs a instance with the specified settings.
21589 		 *
21590 		 * @constructor
21591 		 * @param {Object} settings Name/value object with settings.
21592 		 */
21593 		init: function(settings) {
21594 			var self = this;
21595 
21596 			self._super(settings);
21597 
21598 			if (self.isRtl()) {
21599 				self.addClass('rtl');
21600 			}
21601 
21602 			self.addClass('window');
21603 			self._fixed = true;
21604 
21605 			// Create statusbar
21606 			if (settings.buttons) {
21607 				self.statusbar = new Panel({
21608 					layout: 'flex',
21609 					border: '1 0 0 0',
21610 					spacing: 3,
21611 					padding: 10,
21612 					align: 'center',
21613 					pack: self.isRtl() ? 'start' : 'end',
21614 					defaults: {
21615 						type: 'button'
21616 					},
21617 					items: settings.buttons
21618 				});
21619 
21620 				self.statusbar.addClass('foot');
21621 				self.statusbar.parent(self);
21622 			}
21623 
21624 			self.on('click', function(e) {
21625 				if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
21626 					self.close();
21627 				}
21628 			});
21629 
21630 			self.on('cancel', function() {
21631 				self.close();
21632 			});
21633 
21634 			self.aria('describedby', self.describedBy || self._id + '-none');
21635 			self.aria('label', settings.title);
21636 			self._fullscreen = false;
21637 		},
21638 
21639 		/**
21640 		 * Recalculates the positions of the controls in the current container.
21641 		 * This is invoked by the reflow method and shouldn't be called directly.
21642 		 *
21643 		 * @method recalc
21644 		 */
21645 		recalc: function() {
21646 			var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;
21647 
21648 			if (self._fullscreen) {
21649 				self.layoutRect(DomUtils.getWindowSize());
21650 				self.layoutRect().contentH = self.layoutRect().innerH;
21651 			}
21652 
21653 			self._super();
21654 
21655 			layoutRect = self.layoutRect();
21656 
21657 			// Resize window based on title width
21658 			if (self.settings.title && !self._fullscreen) {
21659 				width = layoutRect.headerW;
21660 				if (width > layoutRect.w) {
21661 					x = layoutRect.x - Math.max(0, width / 2);
21662 					self.layoutRect({w: width, x: x});
21663 					needsRecalc = true;
21664 				}
21665 			}
21666 
21667 			// Resize window based on statusbar width
21668 			if (statusbar) {
21669 				statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();
21670 
21671 				width = statusbar.layoutRect().minW + layoutRect.deltaW;
21672 				if (width > layoutRect.w) {
21673 					x = layoutRect.x - Math.max(0, width - layoutRect.w);
21674 					self.layoutRect({w: width, x: x});
21675 					needsRecalc = true;
21676 				}
21677 			}
21678 
21679 			// Recalc body and disable auto resize
21680 			if (needsRecalc) {
21681 				self.recalc();
21682 			}
21683 		},
21684 
21685 		/**
21686 		 * Initializes the current controls layout rect.
21687 		 * This will be executed by the layout managers to determine the
21688 		 * default minWidth/minHeight etc.
21689 		 *
21690 		 * @method initLayoutRect
21691 		 * @return {Object} Layout rect instance.
21692 		 */
21693 		initLayoutRect: function() {
21694 			var self = this, layoutRect = self._super(), deltaH = 0, headEl;
21695 
21696 			// Reserve vertical space for title
21697 			if (self.settings.title && !self._fullscreen) {
21698 				headEl = self.getEl('head');
21699 
21700 				var size = DomUtils.getSize(headEl);
21701 
21702 				layoutRect.headerW = size.width;
21703 				layoutRect.headerH = size.height;
21704 
21705 				deltaH += layoutRect.headerH;
21706 			}
21707 
21708 			// Reserve vertical space for statusbar
21709 			if (self.statusbar) {
21710 				deltaH += self.statusbar.layoutRect().h;
21711 			}
21712 
21713 			layoutRect.deltaH += deltaH;
21714 			layoutRect.minH += deltaH;
21715 			//layoutRect.innerH -= deltaH;
21716 			layoutRect.h += deltaH;
21717 
21718 			var rect = DomUtils.getWindowSize();
21719 
21720 			layoutRect.x = Math.max(0, rect.w / 2 - layoutRect.w / 2);
21721 			layoutRect.y = Math.max(0, rect.h / 2 - layoutRect.h / 2);
21722 
21723 			return layoutRect;
21724 		},
21725 
21726 		/**
21727 		 * Renders the control as a HTML string.
21728 		 *
21729 		 * @method renderHtml
21730 		 * @return {String} HTML representing the control.
21731 		 */
21732 		renderHtml: function() {
21733 			var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
21734 			var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;
21735 
21736 			self.preRender();
21737 			layout.preRender(self);
21738 
21739 			if (settings.title) {
21740 				headerHtml = (
21741 					'<div id="' + id + '-head" class="' + prefix + 'window-head">' +
21742 						'<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
21743 						'<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>' +
21744 						'<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
21745 					'</div>'
21746 				);
21747 			}
21748 
21749 			if (settings.url) {
21750 				html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
21751 			}
21752 
21753 			if (typeof(html) == "undefined") {
21754 				html = layout.renderHtml(self);
21755 			}
21756 
21757 			if (self.statusbar) {
21758 				footerHtml = self.statusbar.renderHtml();
21759 			}
21760 
21761 			return (
21762 				'<div id="' + id + '" class="' + self.classes() + '" hidefocus="1">' +
21763 					'<div class="' + self.classPrefix + 'reset" role="application">' +
21764 						headerHtml +
21765 						'<div id="' + id + '-body" class="' + self.classes('body') + '">' +
21766 							html +
21767 						'</div>' +
21768 						footerHtml +
21769 					'</div>' +
21770 				'</div>'
21771 			);
21772 		},
21773 
21774 		/**
21775 		 * Switches the window fullscreen mode.
21776 		 *
21777 		 * @method fullscreen
21778 		 * @param {Boolean} state True/false state.
21779 		 * @return {tinymce.ui.Window} Current window instance.
21780 		 */
21781 		fullscreen: function(state) {
21782 			var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;
21783 
21784 			if (state != self._fullscreen) {
21785 				DomUtils.on(window, 'resize', function() {
21786 					var time;
21787 
21788 					if (self._fullscreen) {
21789 						// Time the layout time if it's to slow use a timeout to not hog the CPU
21790 						if (!slowRendering) {
21791 							time = new Date().getTime();
21792 
21793 							var rect = DomUtils.getWindowSize();
21794 							self.moveTo(0, 0).resizeTo(rect.w, rect.h);
21795 
21796 							if ((new Date().getTime()) - time > 50) {
21797 								slowRendering = true;
21798 							}
21799 						} else {
21800 							if (!self._timer) {
21801 								self._timer = setTimeout(function() {
21802 									var rect = DomUtils.getWindowSize();
21803 									self.moveTo(0, 0).resizeTo(rect.w, rect.h);
21804 
21805 									self._timer = 0;
21806 								}, 50);
21807 							}
21808 						}
21809 					}
21810 				});
21811 
21812 				layoutRect = self.layoutRect();
21813 				self._fullscreen = state;
21814 
21815 				if (!state) {
21816 					self._borderBox = self.parseBox(self.settings.border);
21817 					self.getEl('head').style.display = '';
21818 					layoutRect.deltaH += layoutRect.headerH;
21819 					DomUtils.removeClass(documentElement, prefix + 'fullscreen');
21820 					DomUtils.removeClass(document.body, prefix + 'fullscreen');
21821 					self.removeClass('fullscreen');
21822 					self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
21823 				} else {
21824 					self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};
21825 
21826 					self._borderBox = self.parseBox('0');
21827 					self.getEl('head').style.display = 'none';
21828 					layoutRect.deltaH -= layoutRect.headerH + 2;
21829 					DomUtils.addClass(documentElement, prefix + 'fullscreen');
21830 					DomUtils.addClass(document.body, prefix + 'fullscreen');
21831 					self.addClass('fullscreen');
21832 
21833 					var rect = DomUtils.getWindowSize();
21834 					self.moveTo(0, 0).resizeTo(rect.w, rect.h);
21835 				}
21836 			}
21837 
21838 			return self.reflow();
21839 		},
21840 
21841 		/**
21842 		 * Called after the control has been rendered.
21843 		 *
21844 		 * @method postRender
21845 		 */
21846 		postRender: function() {
21847 			var self = this, startPos;
21848 
21849 			setTimeout(function() {
21850 				self.addClass('in');
21851 			}, 0);
21852 
21853 			self._super();
21854 
21855 			if (self.statusbar) {
21856 				self.statusbar.postRender();
21857 			}
21858 
21859 			self.focus();
21860 
21861 			this.dragHelper = new DragHelper(self._id + '-dragh', {
21862 				start: function() {
21863 					startPos = {
21864 						x: self.layoutRect().x,
21865 						y: self.layoutRect().y
21866 					};
21867 				},
21868 
21869 				drag: function(e) {
21870 					self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
21871 				}
21872 			});
21873 
21874 			self.on('submit', function(e) {
21875 				if (!e.isDefaultPrevented()) {
21876 					self.close();
21877 				}
21878 			});
21879 		},
21880 
21881 		/**
21882 		 * Fires a submit event with the serialized form.
21883 		 *
21884 		 * @method submit
21885 		 * @return {Object} Event arguments object.
21886 		 */
21887 		submit: function() {
21888 			return this.fire('submit', {data: this.toJSON()});
21889 		},
21890 
21891 		/**
21892 		 * Removes the current control from DOM and from UI collections.
21893 		 *
21894 		 * @method remove
21895 		 * @return {tinymce.ui.Control} Current control instance.
21896 		 */
21897 		remove: function() {
21898 			var self = this, prefix = self.classPrefix;
21899 
21900 			self.dragHelper.destroy();
21901 			self._super();
21902 
21903 			if (self.statusbar) {
21904 				this.statusbar.remove();
21905 			}
21906 
21907 			if (self._fullscreen) {
21908 				DomUtils.removeClass(document.documentElement, prefix + 'fullscreen');
21909 				DomUtils.removeClass(document.body, prefix + 'fullscreen');
21910 			}
21911 		},
21912 
21913 		/**
21914 		 * Returns the contentWindow object of the iframe if it exists.
21915 		 *
21916 		 * @method getContentWindow
21917 		 * @return {Window} window object or null.
21918 		 */
21919 		getContentWindow: function() {
21920 			var ifr = this.getEl().getElementsByTagName('iframe')[0];
21921 			return ifr ? ifr.contentWindow : null;
21922 		}
21923 	});
21924 
21925 	return Window;
21926 });
21927 
21928 // Included from: js/tinymce/classes/ui/MessageBox.js
21929 
21930 /**
21931  * MessageBox.js
21932  *
21933  * Copyright, Moxiecode Systems AB
21934  * Released under LGPL License.
21935  *
21936  * License: http://www.tinymce.com/license
21937  * Contributing: http://www.tinymce.com/contributing
21938  */
21939 
21940 /**
21941  * This class is used to create MessageBoxes like alerts/confirms etc.
21942  *
21943  * @class tinymce.ui.Window
21944  * @extends tinymce.ui.FloatPanel
21945  */
21946 define("tinymce/ui/MessageBox", [
21947 	"tinymce/ui/Window"
21948 ], function(Window) {
21949 	"use strict";
21950 
21951 	var MessageBox = Window.extend({
21952 		/**
21953 		 * Constructs a instance with the specified settings.
21954 		 *
21955 		 * @constructor
21956 		 * @param {Object} settings Name/value object with settings.
21957 		 */
21958 		init: function(settings) {
21959 			settings = {
21960 				border: 1,
21961 				padding: 20,
21962 				layout: 'flex',
21963 				pack: "center",
21964 				align: "center",
21965 				containerCls: 'panel',
21966 				autoScroll: true,
21967 				buttons: {type: "button", text: "Ok", action: "ok"},
21968 				items: {
21969 					type: "label",
21970 					multiline: true,
21971 					maxWidth: 500,
21972 					maxHeight: 200
21973 				}
21974 			};
21975 
21976 			this._super(settings);
21977 		},
21978 
21979 		Statics: {
21980 			/**
21981 			 * Ok buttons constant.
21982 			 *
21983 			 * @static
21984 			 * @final
21985 			 * @field {Number} OK
21986 			 */
21987 			OK: 1,
21988 
21989 			/**
21990 			 * Ok/cancel buttons constant.
21991 			 *
21992 			 * @static
21993 			 * @final
21994 			 * @field {Number} OK_CANCEL
21995 			 */
21996 			OK_CANCEL: 2,
21997 
21998 			/**
21999 			 * yes/no buttons constant.
22000 			 *
22001 			 * @static
22002 			 * @final
22003 			 * @field {Number} YES_NO
22004 			 */
22005 			YES_NO: 3,
22006 
22007 			/**
22008 			 * yes/no/cancel buttons constant.
22009 			 *
22010 			 * @static
22011 			 * @final
22012 			 * @field {Number} YES_NO_CANCEL
22013 			 */
22014 			YES_NO_CANCEL: 4,
22015 
22016 			/**
22017 			 * Constructs a new message box and renders it to the body element.
22018 			 *
22019 			 * @static
22020 			 * @method msgBox
22021 			 * @param {Object} settings Name/value object with settings.
22022 			 */
22023 			msgBox: function(settings) {
22024 				var buttons, callback = settings.callback || function() {};
22025 
22026 				switch (settings.buttons) {
22027 					case MessageBox.OK_CANCEL:
22028 						buttons = [
22029 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
22030 								e.control.parents()[1].close();
22031 								callback(true);
22032 							}},
22033 
22034 							{type: "button", text: "Cancel", onClick: function(e) {
22035 								e.control.parents()[1].close();
22036 								callback(false);
22037 							}}
22038 						];
22039 						break;
22040 
22041 					case MessageBox.YES_NO:
22042 						buttons = [
22043 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
22044 								e.control.parents()[1].close();
22045 								callback(true);
22046 							}}
22047 						];
22048 						break;
22049 
22050 					case MessageBox.YES_NO_CANCEL:
22051 						buttons = [
22052 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
22053 								e.control.parents()[1].close();
22054 							}}
22055 						];
22056 						break;
22057 
22058 					default:
22059 						buttons = [
22060 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
22061 								e.control.parents()[1].close();
22062 								callback(true);
22063 							}}
22064 						];
22065 						break;
22066 				}
22067 
22068 				return new Window({
22069 					padding: 20,
22070 					x: settings.x,
22071 					y: settings.y,
22072 					minWidth: 300,
22073 					minHeight: 100,
22074 					layout: "flex",
22075 					pack: "center",
22076 					align: "center",
22077 					buttons: buttons,
22078 					title: settings.title,
22079 					role: 'alertdialog',
22080 					items: {
22081 						type: "label",
22082 						multiline: true,
22083 						maxWidth: 500,
22084 						maxHeight: 200,
22085 						text: settings.text
22086 					},
22087 					onPostRender: function() {
22088 						this.aria('describedby', this.items()[0]._id);
22089 					},
22090 					onClose: settings.onClose,
22091 					onCancel: function() {
22092 						callback(false);
22093 					}
22094 				}).renderTo(document.body).reflow();
22095 			},
22096 
22097 			/**
22098 			 * Creates a new alert dialog.
22099 			 *
22100 			 * @method alert
22101 			 * @param {Object} settings Settings for the alert dialog.
22102 			 * @param {function} [callback] Callback to execute when the user makes a choice.
22103 			 */
22104 			alert: function(settings, callback) {
22105 				if (typeof(settings) == "string") {
22106 					settings = {text: settings};
22107 				}
22108 
22109 				settings.callback = callback;
22110 				return MessageBox.msgBox(settings);
22111 			},
22112 
22113 			/**
22114 			 * Creates a new confirm dialog.
22115 			 *
22116 			 * @method confirm
22117 			 * @param {Object} settings Settings for the confirm dialog.
22118 			 * @param {function} [callback] Callback to execute when the user makes a choice.
22119 			 */
22120 			confirm: function(settings, callback) {
22121 				if (typeof(settings) == "string") {
22122 					settings = {text: settings};
22123 				}
22124 
22125 				settings.callback = callback;
22126 				settings.buttons = MessageBox.OK_CANCEL;
22127 
22128 				return MessageBox.msgBox(settings);
22129 			}
22130 		}
22131 	});
22132 
22133 	return MessageBox;
22134 });
22135 
22136 // Included from: js/tinymce/classes/WindowManager.js
22137 
22138 /**
22139  * WindowManager.js
22140  *
22141  * Copyright, Moxiecode Systems AB
22142  * Released under LGPL License.
22143  *
22144  * License: http://www.tinymce.com/license
22145  * Contributing: http://www.tinymce.com/contributing
22146  */
22147 
22148 /**
22149  * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs.
22150  *
22151  * @class tinymce.WindowManager
22152  * @example
22153  * // Opens a new dialog with the file.htm file and the size 320x240
22154  * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
22155  * tinymce.activeEditor.windowManager.open({
22156  *    url: 'file.htm',
22157  *    width: 320,
22158  *    height: 240
22159  * }, {
22160  *    custom_param: 1
22161  * });
22162  *
22163  * // Displays an alert box using the active editors window manager instance
22164  * tinymce.activeEditor.windowManager.alert('Hello world!');
22165  *
22166  * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
22167  * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
22168  *    if (s)
22169  *       tinymce.activeEditor.windowManager.alert("Ok");
22170  *    else
22171  *       tinymce.activeEditor.windowManager.alert("Cancel");
22172  * });
22173  */
22174 define("tinymce/WindowManager", [
22175 	"tinymce/ui/Window",
22176 	"tinymce/ui/MessageBox"
22177 ], function(Window, MessageBox) {
22178 	return function(editor) {
22179 		var self = this, windows = [];
22180 
22181 		function getTopMostWindow() {
22182 			if (windows.length) {
22183 				return windows[windows.length - 1];
22184 			}
22185 		}
22186 
22187 		self.windows = windows;
22188 
22189 		/**
22190 		 * Opens a new window.
22191 		 *
22192 		 * @method open
22193 		 * @param {Object} args Optional name/value settings collection contains things like width/height/url etc.
22194 		 * @option {String} title Window title.
22195 		 * @option {String} file URL of the file to open in the window.
22196 		 * @option {Number} width Width in pixels.
22197 		 * @option {Number} height Height in pixels.
22198 		 * @option {Boolean} resizable Specifies whether the popup window is resizable or not.
22199 		 * @option {Boolean} maximizable Specifies whether the popup window has a "maximize" button and can get maximized or not.
22200 		 * @option {String/Boolean} scrollbars Specifies whether the popup window can have scrollbars if required (i.e. content
22201 		 * larger than the popup size specified).
22202 		 */
22203 		self.open = function(args, params) {
22204 			var win;
22205 
22206 			editor.editorManager.activeEditor = editor;
22207 
22208 			args.title = args.title || ' ';
22209 
22210 			// Handle URL
22211 			args.url = args.url || args.file; // Legacy
22212 			if (args.url) {
22213 				args.width = parseInt(args.width || 320, 10);
22214 				args.height = parseInt(args.height || 240, 10);
22215 			}
22216 
22217 			// Handle body
22218 			if (args.body) {
22219 				args.items = {
22220 					defaults: args.defaults,
22221 					type: args.bodyType || 'form',
22222 					items: args.body
22223 				};
22224 			}
22225 
22226 			if (!args.url && !args.buttons) {
22227 				args.buttons = [
22228 					{text: 'Ok', subtype: 'primary', onclick: function() {
22229 						win.find('form')[0].submit();
22230 					}},
22231 
22232 					{text: 'Cancel', onclick: function() {
22233 						win.close();
22234 					}}
22235 				];
22236 			}
22237 
22238 			win = new Window(args);
22239 			windows.push(win);
22240 
22241 			win.on('close', function() {
22242 				var i = windows.length;
22243 
22244 				while (i--) {
22245 					if (windows[i] === win) {
22246 						windows.splice(i, 1);
22247 					}
22248 				}
22249 
22250 				editor.focus();
22251 			});
22252 
22253 			// Handle data
22254 			if (args.data) {
22255 				win.on('postRender', function() {
22256 					this.find('*').each(function(ctrl) {
22257 						var name = ctrl.name();
22258 
22259 						if (name in args.data) {
22260 							ctrl.value(args.data[name]);
22261 						}
22262 					});
22263 				});
22264 			}
22265 
22266 			// store args and parameters
22267 			win.features = args || {};
22268 			win.params = params || {};
22269 
22270 			// Takes a snapshot in the FocusManager of the selection before focus is lost to dialog
22271 			editor.nodeChanged();
22272 
22273 			return win.renderTo().reflow();
22274 		};
22275 
22276 		/**
22277 		 * Creates a alert dialog. Please don't use the blocking behavior of this
22278 		 * native version use the callback method instead then it can be extended.
22279 		 *
22280 		 * @method alert
22281 		 * @param {String} message Text to display in the new alert dialog.
22282 		 * @param {function} callback Callback function to be executed after the user has selected ok.
22283 		 * @param {Object} scope Optional scope to execute the callback in.
22284 		 * @example
22285 		 * // Displays an alert box using the active editors window manager instance
22286 		 * tinymce.activeEditor.windowManager.alert('Hello world!');
22287 		 */
22288 		self.alert = function(message, callback, scope) {
22289 			MessageBox.alert(message, function() {
22290 				if (callback) {
22291 					callback.call(scope || this);
22292 				} else {
22293 					editor.focus();
22294 				}
22295 			});
22296 		};
22297 
22298 		/**
22299 		 * Creates a confirm dialog. Please don't use the blocking behavior of this
22300 		 * native version use the callback method instead then it can be extended.
22301 		 *
22302 		 * @method confirm
22303 		 * @param {String} messageText to display in the new confirm dialog.
22304 		 * @param {function} callback Callback function to be executed after the user has selected ok or cancel.
22305 		 * @param {Object} scope Optional scope to execute the callback in.
22306 		 * @example
22307 		 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
22308 		 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
22309 		 *    if (s)
22310 		 *       tinymce.activeEditor.windowManager.alert("Ok");
22311 		 *    else
22312 		 *       tinymce.activeEditor.windowManager.alert("Cancel");
22313 		 * });
22314 		 */
22315 		self.confirm = function(message, callback, scope) {
22316 			MessageBox.confirm(message, function(state) {
22317 				callback.call(scope || this, state);
22318 			});
22319 		};
22320 
22321 		/**
22322 		 * Closes the top most window.
22323 		 *
22324 		 * @method close
22325 		 */
22326 		self.close = function() {
22327 			if (getTopMostWindow()) {
22328 				getTopMostWindow().close();
22329 			}
22330 		};
22331 
22332 		/**
22333 		 * Returns the params of the last window open call. This can be used in iframe based
22334 		 * dialog to get params passed from the tinymce plugin.
22335 		 *
22336 		 * @example
22337 		 * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams();
22338 		 *
22339 		 * @method getParams
22340 		 * @return {Object} Name/value object with parameters passed from windowManager.open call.
22341 		 */
22342 		self.getParams = function() {
22343 			return getTopMostWindow() ? getTopMostWindow().params : null;
22344 		};
22345 
22346 		/**
22347 		 * Sets the params of the last opened window.
22348 		 *
22349 		 * @method setParams
22350 		 * @param {Object} params Params object to set for the last opened window.
22351 		 */
22352 		self.setParams = function(params) {
22353 			if (getTopMostWindow()) {
22354 				getTopMostWindow().params = params;
22355 			}
22356 		};
22357 
22358 		/**
22359 		 * Returns the currently opened window objects.
22360 		 *
22361 		 * @method getWindows
22362 		 * @return {Array} Array of the currently opened windows.
22363 		 */
22364 		self.getWindows = function() {
22365 			return windows;
22366 		};
22367 	};
22368 });
22369 
22370 // Included from: js/tinymce/classes/util/Quirks.js
22371 
22372 /**
22373  * Quirks.js
22374  *
22375  * Copyright, Moxiecode Systems AB
22376  * Released under LGPL License.
22377  *
22378  * License: http://www.tinymce.com/license
22379  * Contributing: http://www.tinymce.com/contributing
22380  *
22381  * @ignore-file
22382  */
22383 
22384 /**
22385  * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes.
22386  *
22387  * @class tinymce.util.Quirks
22388  */
22389 define("tinymce/util/Quirks", [
22390 	"tinymce/util/VK",
22391 	"tinymce/dom/RangeUtils",
22392 	"tinymce/html/Node",
22393 	"tinymce/html/Entities",
22394 	"tinymce/Env",
22395 	"tinymce/util/Tools"
22396 ], function(VK, RangeUtils, Node, Entities, Env, Tools) {
22397 	return function(editor) {
22398 		var each = Tools.each;
22399 		var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
22400 			settings = editor.settings, parser = editor.parser, serializer = editor.serializer;
22401 		var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit;
22402 
22403 		/**
22404 		 * Executes a command with a specific state this can be to enable/disable browser editing features.
22405 		 */
22406 		function setEditorCommandState(cmd, state) {
22407 			try {
22408 				editor.getDoc().execCommand(cmd, false, state);
22409 			} catch (ex) {
22410 				// Ignore
22411 			}
22412 		}
22413 
22414 		/**
22415 		 * Returns current IE document mode.
22416 		 */
22417 		function getDocumentMode() {
22418 			var documentMode = editor.getDoc().documentMode;
22419 
22420 			return documentMode ? documentMode : 6;
22421 		}
22422 
22423 		/**
22424 		 * Returns true/false if the event is prevented or not.
22425 		 *
22426 		 * @private
22427 		 * @param {Event} e Event object.
22428 		 * @return {Boolean} true/false if the event is prevented or not.
22429 		 */
22430 		function isDefaultPrevented(e) {
22431 			return e.isDefaultPrevented();
22432 		}
22433 
22434 		/**
22435 		 * Fixes a WebKit bug when deleting contents using backspace or delete key.
22436 		 * WebKit will produce a span element if you delete across two block elements.
22437 		 *
22438 		 * Example:
22439 		 * <h1>a</h1><p>|b</p>
22440 		 *
22441 		 * Will produce this on backspace:
22442 		 * <h1>a<span style="<all runtime styles>">b</span></p>
22443 		 *
22444 		 * This fixes the backspace to produce:
22445 		 * <h1>a|b</p>
22446 		 *
22447 		 * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
22448 		 *
22449 		 * This fixes the following delete scenarios:
22450 		 *  1. Delete by pressing backspace key.
22451 		 *  2. Delete by pressing delete key.
22452 		 *  3. Delete by pressing backspace key with ctrl/cmd (Word delete).
22453 		 *  4. Delete by pressing delete key with ctrl/cmd (Word delete).
22454 		 *  5. Delete by drag/dropping contents inside the editor.
22455 		 *  6. Delete by using Cut Ctrl+X/Cmd+X.
22456 		 *  7. Delete by selecting contents and writing a character.'
22457 		 *
22458 		 * This code is a ugly hack since writing full custom delete logic for just this bug
22459 		 * fix seemed like a huge task. I hope we can remove this before the year 2030. 
22460 		 */
22461 		function cleanupStylesWhenDeleting() {
22462 			var doc = editor.getDoc(), urlPrefix = 'data:text/mce-internal,';
22463 			var MutationObserver = window.MutationObserver, olderWebKit, dragStartRng;
22464 
22465 			// Add mini polyfill for older WebKits
22466 			// TODO: Remove this when old Safari versions gets updated
22467 			if (!MutationObserver) {
22468 				olderWebKit = true;
22469 
22470 				MutationObserver = function() {
22471 					var records = [], target;
22472 
22473 					function nodeInsert(e) {
22474 						var target = e.relatedNode || e.target;
22475 						records.push({target: target, addedNodes: [target]});
22476 					}
22477 
22478 					function attrModified(e) {
22479 						var target = e.relatedNode || e.target;
22480 						records.push({target: target, attributeName: e.attrName});
22481 					}
22482 
22483 					this.observe = function(node) {
22484 						target = node;
22485 						target.addEventListener('DOMSubtreeModified', nodeInsert, false);
22486 						target.addEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
22487 						target.addEventListener('DOMNodeInserted', nodeInsert, false);
22488 						target.addEventListener('DOMAttrModified', attrModified, false);
22489 					};
22490 
22491 					this.disconnect = function() {
22492 						target.removeEventListener('DOMSubtreeModified', nodeInsert, false);
22493 						target.removeEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
22494 						target.removeEventListener('DOMNodeInserted', nodeInsert, false);
22495 						target.removeEventListener('DOMAttrModified', attrModified, false);
22496 					};
22497 
22498 					this.takeRecords = function() {
22499 						return records;
22500 					};
22501 				};
22502 			}
22503 
22504 			function customDelete(isForward) {
22505 				var mutationObserver = new MutationObserver(function() {});
22506 
22507 				Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) {
22508 					// Mark existing spans
22509 					if (elm.tagName == 'SPAN') {
22510 						elm.setAttribute('mce-data-marked', 1);
22511 					}
22512 
22513 					// Make sure all elements has a data-mce-style attribute
22514 					if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
22515 						editor.dom.setAttrib(elm, 'style', elm.getAttribute('style'));
22516 					}
22517 				});
22518 
22519 				// Observe added nodes and style attribute changes
22520 				mutationObserver.observe(editor.getDoc(), {
22521 					childList: true,
22522 					attributes: true,
22523 					subtree: true,
22524 					attributeFilter: ['style']
22525 				});
22526 
22527 				editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null);
22528 
22529 				var rng = editor.selection.getRng();
22530 				var caretElement = rng.startContainer.parentNode;
22531 
22532 				Tools.each(mutationObserver.takeRecords(), function(record) {
22533 					if (!dom.isChildOf(record.target, editor.getBody())) {
22534 						return;
22535 					}
22536 
22537 					// Restore style attribute to previous value
22538 					if (record.attributeName == "style") {
22539 						var oldValue = record.target.getAttribute('data-mce-style');
22540 
22541 						if (oldValue) {
22542 							record.target.setAttribute("style", oldValue);
22543 						} else {
22544 							record.target.removeAttribute("style");
22545 						}
22546 					}
22547 
22548 					// Remove all spans that isn't maked and retain selection
22549 					Tools.each(record.addedNodes, function(node) {
22550 						if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) {
22551 							var offset, container;
22552 
22553 							if (node == caretElement) {
22554 								offset = rng.startOffset;
22555 								container = node.firstChild;
22556 							}
22557 
22558 							dom.remove(node, true);
22559 
22560 							if (container) {
22561 								rng.setStart(container, offset);
22562 								rng.setEnd(container, offset);
22563 								editor.selection.setRng(rng);
22564 							}
22565 						}
22566 					});
22567 				});
22568 
22569 				mutationObserver.disconnect();
22570 
22571 				// Remove any left over marks
22572 				Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) {
22573 					span.removeAttribute('mce-data-marked');
22574 				});
22575 			}
22576 
22577 			editor.on('keydown', function(e) {
22578 				var isForward = e.keyCode == DELETE, isMeta = VK.metaKeyPressed(e);
22579 
22580 				if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
22581 					var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
22582 
22583 					// Ignore non meta delete in the where there is text before/after the caret
22584 					if (!isMeta && rng.collapsed && container.nodeType == 3) {
22585 						if (isForward ? offset < container.data.length : offset > 0) {
22586 							return;
22587 						}
22588 					}
22589 
22590 					e.preventDefault();
22591 
22592 					if (isMeta) {
22593 						editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", "word");
22594 					}
22595 
22596 					customDelete(isForward);
22597 				}
22598 			});
22599 
22600 			editor.on('keypress', function(e) {
22601 				if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode && !VK.metaKeyPressed(e)) {
22602 					e.preventDefault();
22603 					customDelete(true);
22604 					editor.selection.setContent(String.fromCharCode(e.charCode));
22605 				}
22606 			});
22607 
22608 			editor.addCommand('Delete', function() {
22609 				customDelete();
22610 			});
22611 
22612 			editor.addCommand('ForwardDelete', function() {
22613 				customDelete(true);
22614 			});
22615 
22616 			// Older WebKits doesn't properly handle the clipboard so we can't add the rest
22617 			if (olderWebKit) {
22618 				return;
22619 			}
22620 
22621 			editor.on('dragstart', function(e) {
22622 				var selectionHtml;
22623 
22624 				if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') {
22625 					selection.select(e.target);
22626 				}
22627 
22628 				dragStartRng = selection.getRng();
22629 				selectionHtml = editor.selection.getContent();
22630 
22631 				// Safari doesn't support custom dataTransfer items so we can only use URL and Text
22632 				if (selectionHtml.length > 0) {
22633 					e.dataTransfer.setData('URL', 'data:text/mce-internal,' + escape(selectionHtml));
22634 				}
22635 			});
22636 
22637 			editor.on('drop', function(e) {
22638 				if (!isDefaultPrevented(e)) {
22639 					var internalContent = e.dataTransfer.getData('URL');
22640 
22641 					if (!internalContent || internalContent.indexOf(urlPrefix) == -1 || !doc.caretRangeFromPoint) {
22642 						return;
22643 					}
22644 
22645 					internalContent = unescape(internalContent.substr(urlPrefix.length));
22646 					if (doc.caretRangeFromPoint) {
22647 						e.preventDefault();
22648 
22649 						// Safari has a weird issue where drag/dropping images sometimes
22650 						// produces a green plus icon. When this happens the caretRangeFromPoint
22651 						// will return "null" even though the x, y coordinate is correct.
22652 						// But if we detach the insert from the drop event we will get a proper range
22653 						window.setTimeout(function() {
22654 							var pointRng = doc.caretRangeFromPoint(e.x, e.y);
22655 
22656 							if (dragStartRng) {
22657 								selection.setRng(dragStartRng);
22658 								dragStartRng = null;
22659 							}
22660 
22661 							customDelete();
22662 
22663 							selection.setRng(pointRng);
22664 							editor.insertContent(internalContent);
22665 						}, 0);
22666 					}
22667 
22668 				}
22669 			});
22670 
22671 			editor.on('cut', function(e) {
22672 				if (!isDefaultPrevented(e) && e.clipboardData) {
22673 					e.preventDefault();
22674 					e.clipboardData.clearData();
22675 					e.clipboardData.setData('text/html', editor.selection.getContent());
22676 					e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'}));
22677 					customDelete(true);
22678 				}
22679 			});
22680 		}
22681 
22682 		/**
22683 		 * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors.
22684 		 *
22685 		 * For example:
22686 		 * <p><b>|</b></p>
22687 		 *
22688 		 * Or:
22689 		 * <h1>|</h1>
22690 		 *
22691 		 * Or:
22692 		 * [<h1></h1>]
22693 		 */
22694 		function emptyEditorWhenDeleting() {
22695 			function serializeRng(rng) {
22696 				var body = dom.create("body");
22697 				var contents = rng.cloneContents();
22698 				body.appendChild(contents);
22699 				return selection.serializer.serialize(body, {format: 'html'});
22700 			}
22701 
22702 			function allContentsSelected(rng) {
22703 				if (!rng.setStart) {
22704 					if (rng.item) {
22705 						return false;
22706 					}
22707 
22708 					var bodyRng = rng.duplicate();
22709 					bodyRng.moveToElementText(editor.getBody());
22710 					return RangeUtils.compareRanges(rng, bodyRng);
22711 				}
22712 
22713 				var selection = serializeRng(rng);
22714 
22715 				var allRng = dom.createRng();
22716 				allRng.selectNode(editor.getBody());
22717 
22718 				var allSelection = serializeRng(allRng);
22719 				return selection === allSelection;
22720 			}
22721 
22722 			editor.on('keydown', function(e) {
22723 				var keyCode = e.keyCode, isCollapsed, body;
22724 
22725 				// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
22726 				if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
22727 					isCollapsed = editor.selection.isCollapsed();
22728 					body = editor.getBody();
22729 
22730 					// Selection is collapsed but the editor isn't empty
22731 					if (isCollapsed && !dom.isEmpty(body)) {
22732 						return;
22733 					}
22734 
22735 					// Selection isn't collapsed but not all the contents is selected
22736 					if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
22737 						return;
22738 					}
22739 
22740 					// Manually empty the editor
22741 					e.preventDefault();
22742 					editor.setContent('');
22743 
22744 					if (body.firstChild && dom.isBlock(body.firstChild)) {
22745 						editor.selection.setCursorLocation(body.firstChild, 0);
22746 					} else {
22747 						editor.selection.setCursorLocation(body, 0);
22748 					}
22749 
22750 					editor.nodeChanged();
22751 				}
22752 			});
22753 		}
22754 
22755 		/**
22756 		 * WebKit doesn't select all the nodes in the body when you press Ctrl+A.
22757 		 * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438
22758 		 * This selects the whole body so that backspace/delete logic will delete everything
22759 		 */
22760 		function selectAll() {
22761 			editor.on('keydown', function(e) {
22762 				if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {
22763 					e.preventDefault();
22764 					editor.execCommand('SelectAll');
22765 				}
22766 			});
22767 		}
22768 
22769 		/**
22770 		 * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
22771 		 * The IME on Mac doesn't initialize when it doesn't fire a proper focus event.
22772 		 *
22773 		 * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until
22774 		 * you enter a character into the editor.
22775 		 *
22776 		 * It also happens when the first focus in made to the body.
22777 		 *
22778 		 * See: https://bugs.webkit.org/show_bug.cgi?id=83566
22779 		 */
22780 		function inputMethodFocus() {
22781 			if (!editor.settings.content_editable) {
22782 				// Case 1 IME doesn't initialize if you focus the document
22783 				dom.bind(editor.getDoc(), 'focusin', function() {
22784 					selection.setRng(selection.getRng());
22785 				});
22786 
22787 				// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
22788 				dom.bind(editor.getDoc(), 'mousedown', function(e) {
22789 					if (e.target == editor.getDoc().documentElement) {
22790 						editor.getBody().focus();
22791 						selection.setRng(selection.getRng());
22792 					}
22793 				});
22794 			}
22795 		}
22796 
22797 		/**
22798 		 * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
22799 		 * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
22800 		 * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
22801 		 * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
22802 		 * browsers.
22803 		 *
22804 		 * It also fixes a bug on Firefox where it's impossible to delete HR elements.
22805 		 */
22806 		function removeHrOnBackspace() {
22807 			editor.on('keydown', function(e) {
22808 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
22809 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
22810 						var node = selection.getNode();
22811 						var previousSibling = node.previousSibling;
22812 
22813 						if (node.nodeName == 'HR') {
22814 							dom.remove(node);
22815 							e.preventDefault();
22816 							return;
22817 						}
22818 
22819 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
22820 							dom.remove(previousSibling);
22821 							e.preventDefault();
22822 						}
22823 					}
22824 				}
22825 			});
22826 		}
22827 
22828 		/**
22829 		 * Firefox 3.x has an issue where the body element won't get proper focus if you click out
22830 		 * side it's rectangle.
22831 		 */
22832 		function focusBody() {
22833 			// Fix for a focus bug in FF 3.x where the body element
22834 			// wouldn't get proper focus if the user clicked on the HTML element
22835 			if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
22836 				editor.on('mousedown', function(e) {
22837 					if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
22838 						var body = editor.getBody();
22839 
22840 						// Blur the body it's focused but not correctly focused
22841 						body.blur();
22842 
22843 						// Refocus the body after a little while
22844 						setTimeout(function() {
22845 							body.focus();
22846 						}, 0);
22847 					}
22848 				});
22849 			}
22850 		}
22851 
22852 		/**
22853 		 * WebKit has a bug where it isn't possible to select image, hr or anchor elements
22854 		 * by clicking on them so we need to fake that.
22855 		 */
22856 		function selectControlElements() {
22857 			editor.on('click', function(e) {
22858 				e = e.target;
22859 
22860 				// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
22861 				// WebKit can't even do simple things like selecting an image
22862 				// Needs tobe the setBaseAndExtend or it will fail to select floated images
22863 				if (/^(IMG|HR)$/.test(e.nodeName)) {
22864 					selection.getSel().setBaseAndExtent(e, 0, e, 1);
22865 				}
22866 
22867 				if (e.nodeName == 'A' && dom.hasClass(e, 'mce-item-anchor')) {
22868 					selection.select(e);
22869 				}
22870 
22871 				editor.nodeChanged();
22872 			});
22873 		}
22874 
22875 		/**
22876 		 * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements.
22877 		 *
22878 		 * Fixes do backspace/delete on this:
22879 		 * <p>bla[ck</p><p style="color:red">r]ed</p>
22880 		 *
22881 		 * Would become:
22882 		 * <p>bla|ed</p>
22883 		 *
22884 		 * Instead of:
22885 		 * <p style="color:red">bla|ed</p>
22886 		 */
22887 		function removeStylesWhenDeletingAcrossBlockElements() {
22888 			function getAttributeApplyFunction() {
22889 				var template = dom.getAttribs(selection.getStart().cloneNode(false));
22890 
22891 				return function() {
22892 					var target = selection.getStart();
22893 
22894 					if (target !== editor.getBody()) {
22895 						dom.setAttrib(target, "style", null);
22896 
22897 						each(template, function(attr) {
22898 							target.setAttributeNode(attr.cloneNode(true));
22899 						});
22900 					}
22901 				};
22902 			}
22903 
22904 			function isSelectionAcrossElements() {
22905 				return !selection.isCollapsed() &&
22906 					dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
22907 			}
22908 
22909 			editor.on('keypress', function(e) {
22910 				var applyAttributes;
22911 
22912 				if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
22913 					applyAttributes = getAttributeApplyFunction();
22914 					editor.getDoc().execCommand('delete', false, null);
22915 					applyAttributes();
22916 					e.preventDefault();
22917 					return false;
22918 				}
22919 			});
22920 
22921 			dom.bind(editor.getDoc(), 'cut', function(e) {
22922 				var applyAttributes;
22923 
22924 				if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
22925 					applyAttributes = getAttributeApplyFunction();
22926 
22927 					setTimeout(function() {
22928 						applyAttributes();
22929 					}, 0);
22930 				}
22931 			});
22932 		}
22933 
22934 		/**
22935 		 * Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5. It only fires the nodeChange
22936 		 * event every 50ms since it would other wise update the UI when you type and it hogs the CPU.
22937 		 */
22938 		function selectionChangeNodeChanged() {
22939 			var lastRng, selectionTimer;
22940 
22941 			editor.on('selectionchange', function() {
22942 				if (selectionTimer) {
22943 					clearTimeout(selectionTimer);
22944 					selectionTimer = 0;
22945 				}
22946 
22947 				selectionTimer = window.setTimeout(function() {
22948 					if (editor.removed) {
22949 						return;
22950 					}
22951 
22952 					var rng = selection.getRng();
22953 
22954 					// Compare the ranges to see if it was a real change or not
22955 					if (!lastRng || !RangeUtils.compareRanges(rng, lastRng)) {
22956 						editor.nodeChanged();
22957 						lastRng = rng;
22958 					}
22959 				}, 50);
22960 			});
22961 		}
22962 
22963 		/**
22964 		 * Screen readers on IE needs to have the role application set on the body.
22965 		 */
22966 		function ensureBodyHasRoleApplication() {
22967 			document.body.setAttribute("role", "application");
22968 		}
22969 
22970 		/**
22971 		 * Backspacing into a table behaves differently depending upon browser type.
22972 		 * Therefore, disable Backspace when cursor immediately follows a table.
22973 		 */
22974 		function disableBackspaceIntoATable() {
22975 			editor.on('keydown', function(e) {
22976 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
22977 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
22978 						var previousSibling = selection.getNode().previousSibling;
22979 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
22980 							e.preventDefault();
22981 							return false;
22982 						}
22983 					}
22984 				}
22985 			});
22986 		}
22987 
22988 		/**
22989 		 * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this
22990 		 * logic adds a \n before the BR so that it will get rendered.
22991 		 */
22992 		function addNewLinesBeforeBrInPre() {
22993 			// IE8+ rendering mode does the right thing with BR in PRE
22994 			if (getDocumentMode() > 7) {
22995 				return;
22996 			}
22997 
22998 			// Enable display: none in area and add a specific class that hides all BR elements in PRE to
22999 			// avoid the caret from getting stuck at the BR elements while pressing the right arrow key
23000 			setEditorCommandState('RespectVisibilityInDesign', true);
23001 			editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
23002 			dom.addClass(editor.getBody(), 'mceHideBrInPre');
23003 
23004 			// Adds a \n before all BR elements in PRE to get them visual
23005 			parser.addNodeFilter('pre', function(nodes) {
23006 				var i = nodes.length, brNodes, j, brElm, sibling;
23007 
23008 				while (i--) {
23009 					brNodes = nodes[i].getAll('br');
23010 					j = brNodes.length;
23011 					while (j--) {
23012 						brElm = brNodes[j];
23013 
23014 						// Add \n before BR in PRE elements on older IE:s so the new lines get rendered
23015 						sibling = brElm.prev;
23016 						if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
23017 							sibling.value += '\n';
23018 						} else {
23019 							brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n';
23020 						}
23021 					}
23022 				}
23023 			});
23024 
23025 			// Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
23026 			serializer.addNodeFilter('pre', function(nodes) {
23027 				var i = nodes.length, brNodes, j, brElm, sibling;
23028 
23029 				while (i--) {
23030 					brNodes = nodes[i].getAll('br');
23031 					j = brNodes.length;
23032 					while (j--) {
23033 						brElm = brNodes[j];
23034 						sibling = brElm.prev;
23035 						if (sibling && sibling.type == 3) {
23036 							sibling.value = sibling.value.replace(/\r?\n$/, '');
23037 						}
23038 					}
23039 				}
23040 			});
23041 		}
23042 
23043 		/**
23044 		 * Moves style width/height to attribute width/height when the user resizes an image on IE.
23045 		 */
23046 		function removePreSerializedStylesWhenSelectingControls() {
23047 			dom.bind(editor.getBody(), 'mouseup', function() {
23048 				var value, node = selection.getNode();
23049 
23050 				// Moved styles to attributes on IMG eements
23051 				if (node.nodeName == 'IMG') {
23052 					// Convert style width to width attribute
23053 					if ((value = dom.getStyle(node, 'width'))) {
23054 						dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
23055 						dom.setStyle(node, 'width', '');
23056 					}
23057 
23058 					// Convert style height to height attribute
23059 					if ((value = dom.getStyle(node, 'height'))) {
23060 						dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
23061 						dom.setStyle(node, 'height', '');
23062 					}
23063 				}
23064 			});
23065 		}
23066 
23067 		/**
23068 		 * Removes a blockquote when backspace is pressed at the beginning of it.
23069 		 *
23070 		 * For example:
23071 		 * <blockquote><p>|x</p></blockquote>
23072 		 *
23073 		 * Becomes:
23074 		 * <p>|x</p>
23075 		 */
23076 		function removeBlockQuoteOnBackSpace() {
23077 			// Add block quote deletion handler
23078 			editor.on('keydown', function(e) {
23079 				var rng, container, offset, root, parent;
23080 
23081 				if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
23082 					return;
23083 				}
23084 
23085 				rng = selection.getRng();
23086 				container = rng.startContainer;
23087 				offset = rng.startOffset;
23088 				root = dom.getRoot();
23089 				parent = container;
23090 
23091 				if (!rng.collapsed || offset !== 0) {
23092 					return;
23093 				}
23094 
23095 				while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
23096 					parent = parent.parentNode;
23097 				}
23098 
23099 				// Is the cursor at the beginning of a blockquote?
23100 				if (parent.tagName === 'BLOCKQUOTE') {
23101 					// Remove the blockquote
23102 					editor.formatter.toggle('blockquote', null, parent);
23103 
23104 					// Move the caret to the beginning of container
23105 					rng = dom.createRng();
23106 					rng.setStart(container, 0);
23107 					rng.setEnd(container, 0);
23108 					selection.setRng(rng);
23109 				}
23110 			});
23111 		}
23112 
23113 		/**
23114 		 * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc.
23115 		 */
23116 		function setGeckoEditingOptions() {
23117 			function setOpts() {
23118 				editor._refreshContentEditable();
23119 
23120 				setEditorCommandState("StyleWithCSS", false);
23121 				setEditorCommandState("enableInlineTableEditing", false);
23122 
23123 				if (!settings.object_resizing) {
23124 					setEditorCommandState("enableObjectResizing", false);
23125 				}
23126 			}
23127 
23128 			if (!settings.readonly) {
23129 				editor.on('BeforeExecCommand MouseDown', setOpts);
23130 			}
23131 		}
23132 
23133 		/**
23134 		 * Fixes a gecko link bug, when a link is placed at the end of block elements there is
23135 		 * no way to move the caret behind the link. This fix adds a bogus br element after the link.
23136 		 *
23137 		 * For example this:
23138 		 * <p><b><a href="#">x</a></b></p>
23139 		 *
23140 		 * Becomes this:
23141 		 * <p><b><a href="#">x</a></b><br></p>
23142 		 */
23143 		function addBrAfterLastLinks() {
23144 			function fixLinks() {
23145 				each(dom.select('a'), function(node) {
23146 					var parentNode = node.parentNode, root = dom.getRoot();
23147 
23148 					if (parentNode.lastChild === node) {
23149 						while (parentNode && !dom.isBlock(parentNode)) {
23150 							if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
23151 								return;
23152 							}
23153 
23154 							parentNode = parentNode.parentNode;
23155 						}
23156 
23157 						dom.add(parentNode, 'br', {'data-mce-bogus': 1});
23158 					}
23159 				});
23160 			}
23161 
23162 			editor.on('SetContent ExecCommand', function(e) {
23163 				if (e.type == "setcontent" || e.command === 'mceInsertLink') {
23164 					fixLinks();
23165 				}
23166 			});
23167 		}
23168 
23169 		/**
23170 		 * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by
23171 		 * default we want to change that behavior.
23172 		 */
23173 		function setDefaultBlockType() {
23174 			if (settings.forced_root_block) {
23175 				editor.on('init', function() {
23176 					setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
23177 				});
23178 			}
23179 		}
23180 
23181 		/**
23182 		 * Removes ghost selections from images/tables on Gecko.
23183 		 */
23184 		function removeGhostSelection() {
23185 			editor.on('Undo Redo SetContent', function(e) {
23186 				if (!e.initial) {
23187 					editor.execCommand('mceRepaint');
23188 				}
23189 			});
23190 		}
23191 
23192 		/**
23193 		 * Deletes the selected image on IE instead of navigating to previous page.
23194 		 */
23195 		function deleteControlItemOnBackSpace() {
23196 			editor.on('keydown', function(e) {
23197 				var rng;
23198 
23199 				if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
23200 					rng = editor.getDoc().selection.createRange();
23201 					if (rng && rng.item) {
23202 						e.preventDefault();
23203 						editor.undoManager.beforeChange();
23204 						dom.remove(rng.item(0));
23205 						editor.undoManager.add();
23206 					}
23207 				}
23208 			});
23209 		}
23210 
23211 		/**
23212 		 * IE10 doesn't properly render block elements with the right height until you add contents to them.
23213 		 * This fixes that by adding a padding-right to all empty text block elements.
23214 		 * See: https://connect.microsoft.com/IE/feedback/details/743881
23215 		 */
23216 		function renderEmptyBlocksFix() {
23217 			var emptyBlocksCSS;
23218 
23219 			// IE10+
23220 			if (getDocumentMode() >= 10) {
23221 				emptyBlocksCSS = '';
23222 				each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
23223 					emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
23224 				});
23225 
23226 				editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
23227 			}
23228 		}
23229 
23230 		/**
23231 		 * Old IE versions can't retain contents within noscript elements so this logic will store the contents
23232 		 * as a attribute and the insert that value as it's raw text when the DOM is serialized.
23233 		 */
23234 		function keepNoScriptContents() {
23235 			if (getDocumentMode() < 9) {
23236 				parser.addNodeFilter('noscript', function(nodes) {
23237 					var i = nodes.length, node, textNode;
23238 
23239 					while (i--) {
23240 						node = nodes[i];
23241 						textNode = node.firstChild;
23242 
23243 						if (textNode) {
23244 							node.attr('data-mce-innertext', textNode.value);
23245 						}
23246 					}
23247 				});
23248 
23249 				serializer.addNodeFilter('noscript', function(nodes) {
23250 					var i = nodes.length, node, textNode, value;
23251 
23252 					while (i--) {
23253 						node = nodes[i];
23254 						textNode = nodes[i].firstChild;
23255 
23256 						if (textNode) {
23257 							textNode.value = Entities.decode(textNode.value);
23258 						} else {
23259 							// Old IE can't retain noscript value so an attribute is used to store it
23260 							value = node.attributes.map['data-mce-innertext'];
23261 							if (value) {
23262 								node.attr('data-mce-innertext', null);
23263 								textNode = new Node('#text', 3);
23264 								textNode.value = value;
23265 								textNode.raw = true;
23266 								node.append(textNode);
23267 							}
23268 						}
23269 					}
23270 				});
23271 			}
23272 		}
23273 
23274 		/**
23275 		 * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode.
23276 		 */
23277 		function fixCaretSelectionOfDocumentElementOnIe() {
23278 			var doc = dom.doc, body = doc.body, started, startRng, htmlElm;
23279 
23280 			// Return range from point or null if it failed
23281 			function rngFromPoint(x, y) {
23282 				var rng = body.createTextRange();
23283 
23284 				try {
23285 					rng.moveToPoint(x, y);
23286 				} catch (ex) {
23287 					// IE sometimes throws and exception, so lets just ignore it
23288 					rng = null;
23289 				}
23290 
23291 				return rng;
23292 			}
23293 
23294 			// Fires while the selection is changing
23295 			function selectionChange(e) {
23296 				var pointRng;
23297 
23298 				// Check if the button is down or not
23299 				if (e.button) {
23300 					// Create range from mouse position
23301 					pointRng = rngFromPoint(e.x, e.y);
23302 
23303 					if (pointRng) {
23304 						// Check if pointRange is before/after selection then change the endPoint
23305 						if (pointRng.compareEndPoints('StartToStart', startRng) > 0) {
23306 							pointRng.setEndPoint('StartToStart', startRng);
23307 						} else {
23308 							pointRng.setEndPoint('EndToEnd', startRng);
23309 						}
23310 
23311 						pointRng.select();
23312 					}
23313 				} else {
23314 					endSelection();
23315 				}
23316 			}
23317 
23318 			// Removes listeners
23319 			function endSelection() {
23320 				var rng = doc.selection.createRange();
23321 
23322 				// If the range is collapsed then use the last start range
23323 				if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) {
23324 					startRng.select();
23325 				}
23326 
23327 				dom.unbind(doc, 'mouseup', endSelection);
23328 				dom.unbind(doc, 'mousemove', selectionChange);
23329 				startRng = started = 0;
23330 			}
23331 
23332 			// Make HTML element unselectable since we are going to handle selection by hand
23333 			doc.documentElement.unselectable = true;
23334 
23335 			// Detect when user selects outside BODY
23336 			dom.bind(doc, 'mousedown contextmenu', function(e) {
23337 				if (e.target.nodeName === 'HTML') {
23338 					if (started) {
23339 						endSelection();
23340 					}
23341 
23342 					// Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
23343 					htmlElm = doc.documentElement;
23344 					if (htmlElm.scrollHeight > htmlElm.clientHeight) {
23345 						return;
23346 					}
23347 
23348 					started = 1;
23349 					// Setup start position
23350 					startRng = rngFromPoint(e.x, e.y);
23351 					if (startRng) {
23352 						// Listen for selection change events
23353 						dom.bind(doc, 'mouseup', endSelection);
23354 						dom.bind(doc, 'mousemove', selectionChange);
23355 
23356 						dom.getRoot().focus();
23357 						startRng.select();
23358 					}
23359 				}
23360 			});
23361 		}
23362 
23363 		/**
23364 		 * Fixes selection issues where the caret can be placed between two inline elements like <b>a</b>|<b>b</b>
23365 		 * this fix will lean the caret right into the closest inline element.
23366 		 */
23367 		function normalizeSelection() {
23368 			// Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything
23369 			editor.on('keyup focusin mouseup', function(e) {
23370 				if (e.keyCode != 65 || !VK.metaKeyPressed(e)) {
23371 					selection.normalize();
23372 				}
23373 			}, true);
23374 		}
23375 
23376 		/**
23377 		 * Forces Gecko to render a broken image icon if it fails to load an image.
23378 		 */
23379 		function showBrokenImageIcon() {
23380 			editor.contentStyles.push(
23381 				'img:-moz-broken {' +
23382 					'-moz-force-broken-image-icon:1;' +
23383 					'min-width:24px;' +
23384 					'min-height:24px' +
23385 				'}'
23386 			);
23387 		}
23388 
23389 		/**
23390 		 * iOS has a bug where it's impossible to type if the document has a touchstart event
23391 		 * bound and the user touches the document while having the on screen keyboard visible.
23392 		 *
23393 		 * The touch event moves the focus to the parent document while having the caret inside the iframe
23394 		 * this fix moves the focus back into the iframe document.
23395 		 */
23396 		function restoreFocusOnKeyDown() {
23397 			if (!editor.inline) {
23398 				editor.on('keydown', function() {
23399 					if (document.activeElement == document.body) {
23400 						editor.getWin().focus();
23401 					}
23402 				});
23403 			}
23404 		}
23405 
23406 		/**
23407 		 * IE 11 has an annoying issue where you can't move focus into the editor
23408 		 * by clicking on the white area HTML element. We used to be able to to fix this with
23409 		 * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection
23410 		 * object it's not possible anymore. So we need to hack in a ungly CSS to force the
23411 		 * body to be at least 150px. If the user clicks the HTML element out side this 150px region
23412 		 * we simply move the focus into the first paragraph. Not ideal since you loose the
23413 		 * positioning of the caret but goot enough for most cases.
23414 		 */
23415 		function bodyHeight() {
23416 			if (!editor.inline) {
23417 				editor.contentStyles.push('body {min-height: 150px}');
23418 				editor.on('click', function(e) {
23419 					if (e.target.nodeName == 'HTML') {
23420 						editor.getBody().focus();
23421 						editor.selection.normalize();
23422 						editor.nodeChanged();
23423 					}
23424 				});
23425 			}
23426 		}
23427 
23428 		/**
23429 		 * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow.
23430 		 * You might then loose all your work so we need to block that behavior and replace it with our own.
23431 		 */
23432 		function blockCmdArrowNavigation() {
23433 			if (Env.mac) {
23434 				editor.on('keydown', function(e) {
23435 					if (VK.metaKeyPressed(e) && (e.keyCode == 37 || e.keyCode == 39)) {
23436 						e.preventDefault();
23437 						editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'word');
23438 					}
23439 				});
23440 			}
23441 		}
23442 
23443 		/**
23444 		 * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin.
23445 		 */
23446 		function disableAutoUrlDetect() {
23447 			setEditorCommandState("AutoUrlDetect", false);
23448 		}
23449 
23450 		/**
23451 		 * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
23452 		 * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync
23453 		 * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML
23454 		 * but not as the lastChild of the body. However is we add a BR element to the body then remove it
23455 		 * it doesn't seem to add these BR elements makes sence right?!
23456 		 *
23457 		 * Example of what happens: <body>text</body> becomes <body>text<br><br></body>
23458 		 */
23459 		function doubleTrailingBrElements() {
23460 			if (!editor.inline) {
23461 				editor.on('focus blur beforegetcontent', function() {
23462 					var br = editor.dom.create('br');
23463 					editor.getBody().appendChild(br);
23464 					br.parentNode.removeChild(br);
23465 				}, true);
23466 			}
23467 		}
23468 
23469 		/**
23470 		 * iOS 7.1 introduced two new bugs:
23471 		 * 1) It's possible to open links within a contentEditable area by clicking on them.
23472 		 * 2) If you hold down the finger it will display the link/image touch callout menu.
23473 		 */
23474 		function tapLinksAndImages() {
23475 			editor.on('click', function(e) {
23476 				var elm = e.target;
23477 
23478 				do {
23479 					if (elm.tagName === 'A') {
23480 						e.preventDefault();
23481 						return;
23482 					}
23483 				} while ((elm = elm.parentNode));
23484 			});
23485 
23486 			editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');
23487 		}
23488 
23489 		/**
23490 		 * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element.
23491 		 * For example this: <form><button></form>
23492 		 */
23493 		function blockFormSubmitInsideEditor() {
23494 			editor.on('init', function() {
23495 				editor.dom.bind(editor.getBody(), 'submit', function(e) {
23496 					e.preventDefault();
23497 				});
23498 			});
23499 		}
23500 
23501 		// All browsers
23502 		disableBackspaceIntoATable();
23503 		removeBlockQuoteOnBackSpace();
23504 		emptyEditorWhenDeleting();
23505 		normalizeSelection();
23506 
23507 		// WebKit
23508 		if (isWebKit) {
23509 			cleanupStylesWhenDeleting();
23510 			inputMethodFocus();
23511 			selectControlElements();
23512 			setDefaultBlockType();
23513 			blockFormSubmitInsideEditor();
23514 
23515 			// iOS
23516 			if (Env.iOS) {
23517 				selectionChangeNodeChanged();
23518 				restoreFocusOnKeyDown();
23519 				bodyHeight();
23520 				tapLinksAndImages();
23521 			} else {
23522 				selectAll();
23523 			}
23524 		}
23525 
23526 		// IE
23527 		if (isIE && Env.ie < 11) {
23528 			removeHrOnBackspace();
23529 			ensureBodyHasRoleApplication();
23530 			addNewLinesBeforeBrInPre();
23531 			removePreSerializedStylesWhenSelectingControls();
23532 			deleteControlItemOnBackSpace();
23533 			renderEmptyBlocksFix();
23534 			keepNoScriptContents();
23535 			fixCaretSelectionOfDocumentElementOnIe();
23536 		}
23537 
23538 		if (Env.ie >= 11) {
23539 			bodyHeight();
23540 			doubleTrailingBrElements();
23541 		}
23542 
23543 		if (Env.ie) {
23544 			selectAll();
23545 			disableAutoUrlDetect();
23546 		}
23547 
23548 		// Gecko
23549 		if (isGecko) {
23550 			removeHrOnBackspace();
23551 			focusBody();
23552 			removeStylesWhenDeletingAcrossBlockElements();
23553 			setGeckoEditingOptions();
23554 			addBrAfterLastLinks();
23555 			removeGhostSelection();
23556 			showBrokenImageIcon();
23557 			blockCmdArrowNavigation();
23558 		}
23559 	};
23560 });
23561 
23562 // Included from: js/tinymce/classes/util/Observable.js
23563 
23564 /**
23565  * Observable.js
23566  *
23567  * Copyright, Moxiecode Systems AB
23568  * Released under LGPL License.
23569  *
23570  * License: http://www.tinymce.com/license
23571  * Contributing: http://www.tinymce.com/contributing
23572  */
23573 
23574 /**
23575  * This mixin will add event binding logic to classes.
23576  *
23577  * @mixin tinymce.util.Observable
23578  */
23579 define("tinymce/util/Observable", [
23580 	"tinymce/util/EventDispatcher"
23581 ], function(EventDispatcher) {
23582 	function getEventDispatcher(obj) {
23583 		if (!obj._eventDispatcher) {
23584 			obj._eventDispatcher = new EventDispatcher({
23585 				scope: obj,
23586 				toggleEvent: function(name, state) {
23587 					if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) {
23588 						obj.toggleNativeEvent(name, state);
23589 					}
23590 				}
23591 			});
23592 		}
23593 
23594 		return obj._eventDispatcher;
23595 	}
23596 
23597 	return {
23598 		/**
23599 		 * Fires the specified event by name.
23600 		 *
23601 		 * @method fire
23602 		 * @param {String} name Name of the event to fire.
23603 		 * @param {Object?} args Event arguments.
23604 		 * @param {Boolean?} bubble True/false if the event is to be bubbled.
23605 		 * @return {Object} Event args instance passed in.
23606 		 * @example
23607 		 * instance.fire('event', {...});
23608 		 */
23609 		fire: function(name, args, bubble) {
23610 			var self = this;
23611 
23612 			// Prevent all events except the remove event after the instance has been removed
23613 			if (self.removed && name !== "remove") {
23614 				return args;
23615 			}
23616 
23617 			args = getEventDispatcher(self).fire(name, args, bubble);
23618 
23619 			// Bubble event up to parents
23620 			if (bubble !== false && self.parent) {
23621 				var parent = self.parent();
23622 				while (parent && !args.isPropagationStopped()) {
23623 					parent.fire(name, args, false);
23624 					parent = parent.parent();
23625 				}
23626 			}
23627 
23628 			return args;
23629 		},
23630 
23631 		/**
23632 		 * Binds an event listener to a specific event by name.
23633 		 *
23634 		 * @method on
23635 		 * @param {String} name Event name or space separated list of events to bind.
23636 		 * @param {callback} callback Callback to be executed when the event occurs.
23637 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
23638 		 * @return {Object} Current class instance.
23639 		 * @example
23640 		 * instance.on('event', function(e) {
23641 		 *     // Callback logic
23642 		 * });
23643 		 */
23644 		on: function(name, callback, prepend) {
23645 			return getEventDispatcher(this).on(name, callback, prepend);
23646 		},
23647 
23648 		/**
23649 		 * Unbinds an event listener to a specific event by name.
23650 		 *
23651 		 * @method off
23652 		 * @param {String?} name Name of the event to unbind.
23653 		 * @param {callback?} callback Callback to unbind.
23654 		 * @return {Object} Current class instance.
23655 		 * @example
23656 		 * // Unbind specific callback
23657 		 * instance.off('event', handler);
23658 		 *
23659 		 * // Unbind all listeners by name
23660 		 * instance.off('event');
23661 		 *
23662 		 * // Unbind all events
23663 		 * instance.off();
23664 		 */
23665 		off: function(name, callback) {
23666 			return getEventDispatcher(this).off(name, callback);
23667 		},
23668 
23669 		/**
23670 		 * Returns true/false if the object has a event of the specified name.
23671 		 *
23672 		 * @method hasEventListeners
23673 		 * @param {String} name Name of the event to check for.
23674 		 * @return {Boolean} true/false if the event exists or not.
23675 		 */
23676 		hasEventListeners: function(name) {
23677 			return getEventDispatcher(this).has(name);
23678 		}
23679 	};
23680 });
23681 
23682 // Included from: js/tinymce/classes/EditorObservable.js
23683 
23684 /**
23685  * EditorObservable.js
23686  *
23687  * Copyright, Moxiecode Systems AB
23688  * Released under LGPL License.
23689  *
23690  * License: http://www.tinymce.com/license
23691  * Contributing: http://www.tinymce.com/contributing
23692  */
23693 
23694 /**
23695  * This mixin contains the event logic for the tinymce.Editor class.
23696  *
23697  * @mixin tinymce.EditorObservable
23698  * @extends tinymce.util.Observable
23699  */
23700 define("tinymce/EditorObservable", [
23701 	"tinymce/util/Observable",
23702 	"tinymce/dom/DOMUtils",
23703 	"tinymce/util/Tools"
23704 ], function(Observable, DOMUtils, Tools) {
23705 	var DOM = DOMUtils.DOM;
23706 
23707 	function getEventTarget(editor, eventName) {
23708 		if (eventName == 'selectionchange') {
23709 			return editor.getDoc();
23710 		}
23711 
23712 		// Need to bind mousedown/mouseup etc to document not body in iframe mode
23713 		// Since the user might click on the HTML element not the BODY
23714 		if (!editor.inline && /^mouse|click|contextmenu|drop/.test(eventName)) {
23715 			return editor.getDoc();
23716 		}
23717 
23718 		return editor.getBody();
23719 	}
23720 
23721 	function bindEventDelegate(editor, name) {
23722 		var eventRootSelector = editor.settings.event_root, editorManager = editor.editorManager;
23723 		var eventRootElm = editorManager.eventRootElm || getEventTarget(editor, name);
23724 
23725 		if (eventRootSelector) {
23726 			if (!editorManager.rootEvents) {
23727 				editorManager.rootEvents = {};
23728 
23729 				editorManager.on('RemoveEditor', function() {
23730 					if (!editorManager.activeEditor) {
23731 						DOM.unbind(eventRootElm);
23732 						delete editorManager.rootEvents;
23733 					}
23734 				});
23735 			}
23736 
23737 			if (editorManager.rootEvents[name]) {
23738 				return;
23739 			}
23740 
23741 			if (eventRootElm == editor.getBody()) {
23742 				eventRootElm = DOM.select(eventRootSelector)[0];
23743 				editorManager.eventRootElm = eventRootElm;
23744 			}
23745 
23746 			editorManager.rootEvents[name] = true;
23747 
23748 			DOM.bind(eventRootElm, name, function(e) {
23749 				var target = e.target, editors = editorManager.editors, i = editors.length;
23750 
23751 				while (i--) {
23752 					var body = editors[i].getBody();
23753 
23754 					if (body === target || DOM.isChildOf(target, body)) {
23755 						if (!editors[i].hidden) {
23756 							editors[i].fire(name, e);
23757 						}
23758 					}
23759 				}
23760 			});
23761 		} else {
23762 			editor.dom.bind(eventRootElm, name, function(e) {
23763 				if (!editor.hidden) {
23764 					editor.fire(name, e);
23765 				}
23766 			});
23767 		}
23768 	}
23769 
23770 	var EditorObservable = {
23771 		bindPendingEventDelegates: function() {
23772 			var self = this;
23773 
23774 			Tools.each(self._pendingNativeEvents, function(name) {
23775 				bindEventDelegate(self, name);
23776 			});
23777 		},
23778 
23779 		toggleNativeEvent: function(name, state) {
23780 			var self = this;
23781 
23782 			if (self.settings.readonly) {
23783 				return;
23784 			}
23785 
23786 			// Never bind focus/blur since the FocusManager fakes those
23787 			if (name == "focus" || name == "blur") {
23788 				return;
23789 			}
23790 
23791 			if (state) {
23792 				if (self.initialized) {
23793 					bindEventDelegate(self, name);
23794 				} else {
23795 					if (!self._pendingNativeEvents) {
23796 						self._pendingNativeEvents = [name];
23797 					} else {
23798 						self._pendingNativeEvents.push(name);
23799 					}
23800 				}
23801 			} else if (self.initialized) {
23802 				self.dom.unbind(getEventTarget(self, name), name);
23803 			}
23804 		}
23805 	};
23806 
23807 	EditorObservable = Tools.extend({}, Observable, EditorObservable);
23808 
23809 	return EditorObservable;
23810 });
23811 
23812 // Included from: js/tinymce/classes/Shortcuts.js
23813 
23814 /**
23815  * Shortcuts.js
23816  *
23817  * Copyright, Moxiecode Systems AB
23818  * Released under LGPL License.
23819  *
23820  * License: http://www.tinymce.com/license
23821  * Contributing: http://www.tinymce.com/contributing
23822  */
23823 
23824 /**
23825  * Contains all logic for handling of keyboard shortcuts.
23826  */
23827 define("tinymce/Shortcuts", [
23828 	"tinymce/util/Tools",
23829 	"tinymce/Env"
23830 ], function(Tools, Env) {
23831 	var each = Tools.each, explode = Tools.explode;
23832 
23833 	var keyCodeLookup = {
23834 		"f9": 120,
23835 		"f10": 121,
23836 		"f11": 122
23837 	};
23838 
23839 	return function(editor) {
23840 		var self = this, shortcuts = {};
23841 
23842 		editor.on('keyup keypress keydown', function(e) {
23843 			if (e.altKey || e.ctrlKey || e.metaKey) {
23844 				each(shortcuts, function(shortcut) {
23845 					var ctrlKey = Env.mac ? e.metaKey : e.ctrlKey;
23846 
23847 					if (shortcut.ctrl != ctrlKey || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
23848 						return;
23849 					}
23850 
23851 					if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
23852 						e.preventDefault();
23853 
23854 						if (e.type == "keydown") {
23855 							shortcut.func.call(shortcut.scope);
23856 						}
23857 
23858 						return true;
23859 					}
23860 				});
23861 			}
23862 		});
23863 
23864 		/**
23865 		 * Adds a keyboard shortcut for some command or function.
23866 		 *
23867 		 * @method addShortcut
23868 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
23869 		 * @param {String} desc Text description for the command.
23870 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
23871 		 * @param {Object} sc Optional scope to execute the function in.
23872 		 * @return {Boolean} true/false state if the shortcut was added or not.
23873 		 */
23874 		self.add = function(pattern, desc, cmdFunc, scope) {
23875 			var cmd;
23876 
23877 			cmd = cmdFunc;
23878 
23879 			if (typeof(cmdFunc) === 'string') {
23880 				cmdFunc = function() {
23881 					editor.execCommand(cmd, false, null);
23882 				};
23883 			} else if (Tools.isArray(cmd)) {
23884 				cmdFunc = function() {
23885 					editor.execCommand(cmd[0], cmd[1], cmd[2]);
23886 				};
23887 			}
23888 
23889 			each(explode(pattern.toLowerCase()), function(pattern) {
23890 				var shortcut = {
23891 					func: cmdFunc,
23892 					scope: scope || editor,
23893 					desc: editor.translate(desc),
23894 					alt: false,
23895 					ctrl: false,
23896 					shift: false
23897 				};
23898 
23899 				each(explode(pattern, '+'), function(value) {
23900 					switch (value) {
23901 						case 'alt':
23902 						case 'ctrl':
23903 						case 'shift':
23904 							shortcut[value] = true;
23905 							break;
23906 
23907 						default:
23908 							shortcut.charCode = value.charCodeAt(0);
23909 							shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
23910 					}
23911 				});
23912 
23913 				shortcuts[
23914 					(shortcut.ctrl ? 'ctrl' : '') + ',' +
23915 					(shortcut.alt ? 'alt' : '') + ',' +
23916 					(shortcut.shift ? 'shift' : '') + ',' +
23917 					shortcut.keyCode
23918 				] = shortcut;
23919 			});
23920 
23921 			return true;
23922 		};
23923 	};
23924 });
23925 
23926 // Included from: js/tinymce/classes/Editor.js
23927 
23928 /**
23929  * Editor.js
23930  *
23931  * Copyright, Moxiecode Systems AB
23932  * Released under LGPL License.
23933  *
23934  * License: http://www.tinymce.com/license
23935  * Contributing: http://www.tinymce.com/contributing
23936  */
23937 
23938 /*jshint scripturl:true */
23939 
23940 /**
23941  * Include the base event class documentation.
23942  *
23943  * @include ../../../tools/docs/tinymce.Event.js
23944  */
23945 
23946 /**
23947  * This class contains the core logic for a TinyMCE editor.
23948  *
23949  * @class tinymce.Editor
23950  * @mixes tinymce.util.Observable
23951  * @example
23952  * // Add a class to all paragraphs in the editor.
23953  * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
23954  *
23955  * // Gets the current editors selection as text
23956  * tinymce.activeEditor.selection.getContent({format: 'text'});
23957  *
23958  * // Creates a new editor instance
23959  * var ed = new tinymce.Editor('textareaid', {
23960  *     some_setting: 1
23961  * }, tinymce.EditorManager);
23962  *
23963  * // Select each item the user clicks on
23964  * ed.on('click', function(e) {
23965  *     ed.selection.select(e.target);
23966  * });
23967  *
23968  * ed.render();
23969  */
23970 define("tinymce/Editor", [
23971 	"tinymce/dom/DOMUtils",
23972 	"tinymce/AddOnManager",
23973 	"tinymce/html/Node",
23974 	"tinymce/dom/Serializer",
23975 	"tinymce/html/Serializer",
23976 	"tinymce/dom/Selection",
23977 	"tinymce/Formatter",
23978 	"tinymce/UndoManager",
23979 	"tinymce/EnterKey",
23980 	"tinymce/ForceBlocks",
23981 	"tinymce/EditorCommands",
23982 	"tinymce/util/URI",
23983 	"tinymce/dom/ScriptLoader",
23984 	"tinymce/dom/EventUtils",
23985 	"tinymce/WindowManager",
23986 	"tinymce/html/Schema",
23987 	"tinymce/html/DomParser",
23988 	"tinymce/util/Quirks",
23989 	"tinymce/Env",
23990 	"tinymce/util/Tools",
23991 	"tinymce/EditorObservable",
23992 	"tinymce/Shortcuts"
23993 ], function(
23994 	DOMUtils, AddOnManager, Node, DomSerializer, Serializer,
23995 	Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
23996 	URI, ScriptLoader, EventUtils, WindowManager,
23997 	Schema, DomParser, Quirks, Env, Tools, EditorObservable, Shortcuts
23998 ) {
23999 	// Shorten these names
24000 	var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
24001 	var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
24002 	var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
24003 	var Event = EventUtils.Event;
24004 	var isGecko = Env.gecko, ie = Env.ie;
24005 
24006 	/**
24007 	 * Include documentation for all the events.
24008 	 *
24009 	 * @include ../../../tools/docs/tinymce.Editor.js
24010 	 */
24011 
24012 	/**
24013 	 * Constructs a editor instance by id.
24014 	 *
24015 	 * @constructor
24016 	 * @method Editor
24017 	 * @param {String} id Unique id for the editor.
24018 	 * @param {Object} settings Settings for the editor.
24019 	 * @param {tinymce.EditorManager} editorManager EditorManager instance.
24020 	 * @author Moxiecode
24021 	 */
24022 	function Editor(id, settings, editorManager) {
24023 		var self = this, documentBaseUrl, baseUri;
24024 
24025 		documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
24026 		baseUri = editorManager.baseURI;
24027 
24028 		/**
24029 		 * Name/value collection with editor settings.
24030 		 *
24031 		 * @property settings
24032 		 * @type Object
24033 		 * @example
24034 		 * // Get the value of the theme setting
24035 		 * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
24036 		 */
24037 		self.settings = settings = extend({
24038 			id: id,
24039 			theme: 'modern',
24040 			delta_width: 0,
24041 			delta_height: 0,
24042 			popup_css: '',
24043 			plugins: '',
24044 			document_base_url: documentBaseUrl,
24045 			add_form_submit_trigger: true,
24046 			submit_patch: true,
24047 			add_unload_trigger: true,
24048 			convert_urls: true,
24049 			relative_urls: true,
24050 			remove_script_host: true,
24051 			object_resizing: true,
24052 			doctype: '<!DOCTYPE html>',
24053 			visual: true,
24054 			font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
24055 
24056 			// See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
24057 			font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
24058 			forced_root_block: 'p',
24059 			hidden_input: true,
24060 			padd_empty_editor: true,
24061 			render_ui: true,
24062 			indentation: '30px',
24063 			inline_styles: true,
24064 			convert_fonts_to_spans: true,
24065 			indent: 'simple',
24066 			indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,' +
24067 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
24068 			indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,' +
24069 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
24070 			validate: true,
24071 			entity_encoding: 'named',
24072 			url_converter: self.convertURL,
24073 			url_converter_scope: self,
24074 			ie7_compat: true
24075 		}, settings);
24076 
24077 		AddOnManager.language = settings.language || 'en';
24078 		AddOnManager.languageLoad = settings.language_load;
24079 
24080 		AddOnManager.baseURL = editorManager.baseURL;
24081 
24082 		/**
24083 		 * Editor instance id, normally the same as the div/textarea that was replaced.
24084 		 *
24085 		 * @property id
24086 		 * @type String
24087 		 */
24088 		self.id = settings.id = id;
24089 
24090 		/**
24091 		 * State to force the editor to return false on a isDirty call.
24092 		 *
24093 		 * @property isNotDirty
24094 		 * @type Boolean
24095 		 * @example
24096 		 * function ajaxSave() {
24097 		 *     var ed = tinymce.get('elm1');
24098 		 *
24099 		 *     // Save contents using some XHR call
24100 		 *     alert(ed.getContent());
24101 		 *
24102 		 *     ed.isNotDirty = true; // Force not dirty state
24103 		 * }
24104 		 */
24105 		self.isNotDirty = true;
24106 
24107 		/**
24108 		 * Name/Value object containting plugin instances.
24109 		 *
24110 		 * @property plugins
24111 		 * @type Object
24112 		 * @example
24113 		 * // Execute a method inside a plugin directly
24114 		 * tinymce.activeEditor.plugins.someplugin.someMethod();
24115 		 */
24116 		self.plugins = {};
24117 
24118 		/**
24119 		 * URI object to document configured for the TinyMCE instance.
24120 		 *
24121 		 * @property documentBaseURI
24122 		 * @type tinymce.util.URI
24123 		 * @example
24124 		 * // Get relative URL from the location of document_base_url
24125 		 * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
24126 		 *
24127 		 * // Get absolute URL from the location of document_base_url
24128 		 * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
24129 		 */
24130 		self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
24131 			base_uri: baseUri
24132 		});
24133 
24134 		/**
24135 		 * URI object to current document that holds the TinyMCE editor instance.
24136 		 *
24137 		 * @property baseURI
24138 		 * @type tinymce.util.URI
24139 		 * @example
24140 		 * // Get relative URL from the location of the API
24141 		 * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
24142 		 *
24143 		 * // Get absolute URL from the location of the API
24144 		 * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
24145 		 */
24146 		self.baseURI = baseUri;
24147 
24148 		/**
24149 		 * Array with CSS files to load into the iframe.
24150 		 *
24151 		 * @property contentCSS
24152 		 * @type Array
24153 		 */
24154 		self.contentCSS = [];
24155 
24156 		/**
24157 		 * Array of CSS styles to add to head of document when the editor loads.
24158 		 *
24159 		 * @property contentStyles
24160 		 * @type Array
24161 		 */
24162 		self.contentStyles = [];
24163 
24164 		// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
24165 		self.shortcuts = new Shortcuts(self);
24166 
24167 		// Internal command handler objects
24168 		self.execCommands = {};
24169 		self.queryStateCommands = {};
24170 		self.queryValueCommands = {};
24171 		self.loadedCSS = {};
24172 
24173 		self.suffix = editorManager.suffix;
24174 		self.editorManager = editorManager;
24175 		self.inline = settings.inline;
24176 
24177 		// Call setup
24178 		editorManager.fire('SetupEditor', self);
24179 		self.execCallback('setup', self);
24180 	}
24181 
24182 	Editor.prototype = {
24183 		/**
24184 		 * Renderes the editor/adds it to the page.
24185 		 *
24186 		 * @method render
24187 		 */
24188 		render: function() {
24189 			var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
24190 
24191 			function readyHandler() {
24192 				DOM.unbind(window, 'ready', readyHandler);
24193 				self.render();
24194 			}
24195 
24196 			// Page is not loaded yet, wait for it
24197 			if (!Event.domLoaded) {
24198 				DOM.bind(window, 'ready', readyHandler);
24199 				return;
24200 			}
24201 
24202 			// Element not found, then skip initialization
24203 			if (!self.getElement()) {
24204 				return;
24205 			}
24206 
24207 			// No editable support old iOS versions etc
24208 			if (!Env.contentEditable) {
24209 				return;
24210 			}
24211 
24212 			// Hide target element early to prevent content flashing
24213 			if (!settings.inline) {
24214 				self.orgVisibility = self.getElement().style.visibility;
24215 				self.getElement().style.visibility = 'hidden';
24216 			} else {
24217 				self.inline = true;
24218 			}
24219 
24220 			var form = self.getElement().form || DOM.getParent(id, 'form');
24221 			if (form) {
24222 				self.formElement = form;
24223 
24224 				// Add hidden input for non input elements inside form elements
24225 				if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
24226 					DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
24227 					self.hasHiddenInput = true;
24228 				}
24229 
24230 				// Pass submit/reset from form to editor instance
24231 				self.formEventDelegate = function(e) {
24232 					self.fire(e.type, e);
24233 				};
24234 
24235 				DOM.bind(form, 'submit reset', self.formEventDelegate);
24236 
24237 				// Reset contents in editor when the form is reset
24238 				self.on('reset', function() {
24239 					self.setContent(self.startContent, {format: 'raw'});
24240 				});
24241 
24242 				// Check page uses id="submit" or name="submit" for it's submit button
24243 				if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
24244 					form._mceOldSubmit = form.submit;
24245 					form.submit = function() {
24246 						self.editorManager.triggerSave();
24247 						self.isNotDirty = true;
24248 
24249 						return form._mceOldSubmit(form);
24250 					};
24251 				}
24252 			}
24253 
24254 			/**
24255 			 * Window manager reference, use this to open new windows and dialogs.
24256 			 *
24257 			 * @property windowManager
24258 			 * @type tinymce.WindowManager
24259 			 * @example
24260 			 * // Shows an alert message
24261 			 * tinymce.activeEditor.windowManager.alert('Hello world!');
24262 			 *
24263 			 * // Opens a new dialog with the file.htm file and the size 320x240
24264 			 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
24265 			 * tinymce.activeEditor.windowManager.open({
24266 			 *    url: 'file.htm',
24267 			 *    width: 320,
24268 			 *    height: 240
24269 			 * }, {
24270 			 *    custom_param: 1
24271 			 * });
24272 			 */
24273 			self.windowManager = new WindowManager(self);
24274 
24275 			if (settings.encoding == 'xml') {
24276 				self.on('GetContent', function(e) {
24277 					if (e.save) {
24278 						e.content = DOM.encode(e.content);
24279 					}
24280 				});
24281 			}
24282 
24283 			if (settings.add_form_submit_trigger) {
24284 				self.on('submit', function() {
24285 					if (self.initialized) {
24286 						self.save();
24287 					}
24288 				});
24289 			}
24290 
24291 			if (settings.add_unload_trigger) {
24292 				self._beforeUnload = function() {
24293 					if (self.initialized && !self.destroyed && !self.isHidden()) {
24294 						self.save({format: 'raw', no_events: true, set_dirty: false});
24295 					}
24296 				};
24297 
24298 				self.editorManager.on('BeforeUnload', self._beforeUnload);
24299 			}
24300 
24301 			// Load scripts
24302 			function loadScripts() {
24303 				var scriptLoader = ScriptLoader.ScriptLoader;
24304 
24305 				if (settings.language && settings.language != 'en' && !settings.language_url) {
24306 					settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
24307 				}
24308 
24309 				if (settings.language_url) {
24310 					scriptLoader.add(settings.language_url);
24311 				}
24312 
24313 				if (settings.theme && typeof settings.theme != "function" &&
24314 					settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
24315 					var themeUrl = settings.theme_url;
24316 
24317 					if (themeUrl) {
24318 						themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
24319 					} else {
24320 						themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
24321 					}
24322 
24323 					ThemeManager.load(settings.theme, themeUrl);
24324 				}
24325 
24326 				if (Tools.isArray(settings.plugins)) {
24327 					settings.plugins = settings.plugins.join(' ');
24328 				}
24329 
24330 				each(settings.external_plugins, function(url, name) {
24331 					PluginManager.load(name, url);
24332 					settings.plugins += ' ' + name;
24333 				});
24334 
24335 				each(settings.plugins.split(/[ ,]/), function(plugin) {
24336 					plugin = trim(plugin);
24337 
24338 					if (plugin && !PluginManager.urls[plugin]) {
24339 						if (plugin.charAt(0) == '-') {
24340 							plugin = plugin.substr(1, plugin.length);
24341 
24342 							var dependencies = PluginManager.dependencies(plugin);
24343 
24344 							each(dependencies, function(dep) {
24345 								var defaultSettings = {
24346 									prefix:'plugins/',
24347 									resource: dep,
24348 									suffix:'/plugin' + suffix + '.js'
24349 								};
24350 
24351 								dep = PluginManager.createUrl(defaultSettings, dep);
24352 								PluginManager.load(dep.resource, dep);
24353 							});
24354 						} else {
24355 							PluginManager.load(plugin, {
24356 								prefix: 'plugins/',
24357 								resource: plugin,
24358 								suffix: '/plugin' + suffix + '.js'
24359 							});
24360 						}
24361 					}
24362 				});
24363 
24364 				scriptLoader.loadQueue(function() {
24365 					if (!self.removed) {
24366 						self.init();
24367 					}
24368 				});
24369 			}
24370 
24371 			loadScripts();
24372 		},
24373 
24374 		/**
24375 		 * Initializes the editor this will be called automatically when
24376 		 * all plugins/themes and language packs are loaded by the rendered method.
24377 		 * This method will setup the iframe and create the theme and plugin instances.
24378 		 *
24379 		 * @method init
24380 		 */
24381 		init: function() {
24382 			var self = this, settings = self.settings, elm = self.getElement();
24383 			var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = [];
24384 
24385 			self.rtl = this.editorManager.i18n.rtl;
24386 			self.editorManager.add(self);
24387 
24388 			settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
24389 
24390 			/**
24391 			 * Reference to the theme instance that was used to generate the UI.
24392 			 *
24393 			 * @property theme
24394 			 * @type tinymce.Theme
24395 			 * @example
24396 			 * // Executes a method on the theme directly
24397 			 * tinymce.activeEditor.theme.someMethod();
24398 			 */
24399 			if (settings.theme) {
24400 				if (typeof settings.theme != "function") {
24401 					settings.theme = settings.theme.replace(/-/, '');
24402 					Theme = ThemeManager.get(settings.theme);
24403 					self.theme = new Theme(self, ThemeManager.urls[settings.theme]);
24404 
24405 					if (self.theme.init) {
24406 						self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''));
24407 					}
24408 				} else {
24409 					self.theme = settings.theme;
24410 				}
24411 			}
24412 
24413 			function initPlugin(plugin) {
24414 				var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance;
24415 
24416 				pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
24417 				plugin = trim(plugin);
24418 				if (Plugin && inArray(initializedPlugins, plugin) === -1) {
24419 					each(PluginManager.dependencies(plugin), function(dep){
24420 						initPlugin(dep);
24421 					});
24422 
24423 					pluginInstance = new Plugin(self, pluginUrl);
24424 
24425 					self.plugins[plugin] = pluginInstance;
24426 
24427 					if (pluginInstance.init) {
24428 						pluginInstance.init(self, pluginUrl);
24429 						initializedPlugins.push(plugin);
24430 					}
24431 				}
24432 			}
24433 
24434 			// Create all plugins
24435 			each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
24436 
24437 			// Measure box
24438 			if (settings.render_ui && self.theme) {
24439 				self.orgDisplay = elm.style.display;
24440 
24441 				if (typeof settings.theme != "function") {
24442 					w = settings.width || elm.style.width || elm.offsetWidth;
24443 					h = settings.height || elm.style.height || elm.offsetHeight;
24444 					minHeight = settings.min_height || 100;
24445 					re = /^[0-9\.]+(|px)$/i;
24446 
24447 					if (re.test('' + w)) {
24448 						w = Math.max(parseInt(w, 10), 100);
24449 					}
24450 
24451 					if (re.test('' + h)) {
24452 						h = Math.max(parseInt(h, 10), minHeight);
24453 					}
24454 
24455 					// Render UI
24456 					o = self.theme.renderUI({
24457 						targetNode: elm,
24458 						width: w,
24459 						height: h,
24460 						deltaWidth: settings.delta_width,
24461 						deltaHeight: settings.delta_height
24462 					});
24463 
24464 					// Resize editor
24465 					if (!settings.content_editable) {
24466 						DOM.setStyles(o.sizeContainer || o.editorContainer, {
24467 							wi2dth: w,
24468 							// TODO: Fix this
24469 							h2eight: h
24470 						});
24471 
24472 						h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
24473 						if (h < minHeight) {
24474 							h = minHeight;
24475 						}
24476 					}
24477 				} else {
24478 					o = settings.theme(self, elm);
24479 
24480 					// Convert element type to id:s
24481 					if (o.editorContainer.nodeType) {
24482 						o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
24483 					}
24484 
24485 					// Convert element type to id:s
24486 					if (o.iframeContainer.nodeType) {
24487 						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
24488 					}
24489 
24490 					// Use specified iframe height or the targets offsetHeight
24491 					h = o.iframeHeight || elm.offsetHeight;
24492 				}
24493 
24494 				self.editorContainer = o.editorContainer;
24495 			}
24496 
24497 			// Load specified content CSS last
24498 			if (settings.content_css) {
24499 				each(explode(settings.content_css), function(u) {
24500 					self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
24501 				});
24502 			}
24503 
24504 			// Load specified content CSS last
24505 			if (settings.content_style) {
24506 				self.contentStyles.push(settings.content_style);
24507 			}
24508 
24509 			// Content editable mode ends here
24510 			if (settings.content_editable) {
24511 				elm = n = o = null; // Fix IE leak
24512 				return self.initContentBody();
24513 			}
24514 
24515 			self.iframeHTML = settings.doctype + '<html><head>';
24516 
24517 			// We only need to override paths if we have to
24518 			// IE has a bug where it remove site absolute urls to relative ones if this is specified
24519 			if (settings.document_base_url != self.documentBaseUrl) {
24520 				self.iframeHTML += '<base href="' + self.documentBaseURI.getURI() + '" />';
24521 			}
24522 
24523 			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
24524 			if (!Env.caretAfter && settings.ie7_compat) {
24525 				self.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
24526 			}
24527 
24528 			self.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
24529 
24530 			// Load the CSS by injecting them into the HTML this will reduce "flicker"
24531 			for (i = 0; i < self.contentCSS.length; i++) {
24532 				var cssUrl = self.contentCSS[i];
24533 				self.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + cssUrl + '" />';
24534 				self.loadedCSS[cssUrl] = true;
24535 			}
24536 
24537 			bodyId = settings.body_id || 'tinymce';
24538 			if (bodyId.indexOf('=') != -1) {
24539 				bodyId = self.getParam('body_id', '', 'hash');
24540 				bodyId = bodyId[self.id] || bodyId;
24541 			}
24542 
24543 			bodyClass = settings.body_class || '';
24544 			if (bodyClass.indexOf('=') != -1) {
24545 				bodyClass = self.getParam('body_class', '', 'hash');
24546 				bodyClass = bodyClass[self.id] || '';
24547 			}
24548 
24549 			self.iframeHTML += '</head><body id="' + bodyId + '" class="mce-content-body ' + bodyClass + '" ' +
24550 				'onload="window.parent.tinymce.get(\'' + self.id + '\').fire(\'load\');"><br></body></html>';
24551 
24552 			/*eslint no-script-url:0 */
24553 			var domainRelaxUrl = 'javascript:(function(){' +
24554 				'document.open();document.domain="' + document.domain + '";' +
24555 				'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
24556 				'document.close();ed.initContentBody(true);})()';
24557 
24558 			// Domain relaxing is required since the user has messed around with document.domain
24559 			if (document.domain != location.hostname) {
24560 				url = domainRelaxUrl;
24561 			}
24562 
24563 			// Create iframe
24564 			// TODO: ACC add the appropriate description on this.
24565 			n = DOM.add(o.iframeContainer, 'iframe', {
24566 				id: self.id + "_ifr",
24567 				src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
24568 				frameBorder: '0',
24569 				allowTransparency: "true",
24570 				title: self.editorManager.translate(
24571 					"Rich Text Area. Press ALT-F9 for menu. " +
24572 					"Press ALT-F10 for toolbar. Press ALT-0 for help"
24573 				),
24574 				style: {
24575 					width: '100%',
24576 					height: h,
24577 					display: 'block' // Important for Gecko to render the iframe correctly
24578 				}
24579 			});
24580 
24581 			// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
24582 			// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
24583 			if (ie) {
24584 				try {
24585 					self.getDoc();
24586 				} catch (e) {
24587 					n.src = url = domainRelaxUrl;
24588 				}
24589 			}
24590 
24591 			self.contentAreaContainer = o.iframeContainer;
24592 
24593 			if (o.editorContainer) {
24594 				DOM.get(o.editorContainer).style.display = self.orgDisplay;
24595 			}
24596 
24597 			DOM.get(self.id).style.display = 'none';
24598 			DOM.setAttrib(self.id, 'aria-hidden', true);
24599 
24600 			if (!url) {
24601 				self.initContentBody();
24602 			}
24603 
24604 			elm = n = o = null; // Cleanup
24605 		},
24606 
24607 		/**
24608 		 * This method get called by the init method ones the iframe is loaded.
24609 		 * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
24610 		 *
24611 		 * @method initContentBody
24612 		 * @private
24613 		 */
24614 		initContentBody: function(skipWrite) {
24615 			var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), body, contentCssText;
24616 
24617 			// Restore visibility on target element
24618 			if (!settings.inline) {
24619 				self.getElement().style.visibility = self.orgVisibility;
24620 			}
24621 
24622 			// Setup iframe body
24623 			if (!skipWrite && !settings.content_editable) {
24624 				doc.open();
24625 				doc.write(self.iframeHTML);
24626 				doc.close();
24627 			}
24628 
24629 			if (settings.content_editable) {
24630 				self.on('remove', function() {
24631 					var bodyEl = this.getBody();
24632 
24633 					DOM.removeClass(bodyEl, 'mce-content-body');
24634 					DOM.removeClass(bodyEl, 'mce-edit-focus');
24635 					DOM.setAttrib(bodyEl, 'contentEditable', null);
24636 				});
24637 
24638 				DOM.addClass(targetElm, 'mce-content-body');
24639 				self.contentDocument = doc = settings.content_document || document;
24640 				self.contentWindow = settings.content_window || window;
24641 				self.bodyElement = targetElm;
24642 
24643 				// Prevent leak in IE
24644 				settings.content_document = settings.content_window = null;
24645 
24646 				// TODO: Fix this
24647 				settings.root_name = targetElm.nodeName.toLowerCase();
24648 			}
24649 
24650 			// It will not steal focus while setting contentEditable
24651 			body = self.getBody();
24652 			body.disabled = true;
24653 
24654 			if (!settings.readonly) {
24655 				if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
24656 					body.style.position = 'relative';
24657 				}
24658 
24659 				body.contentEditable = self.getParam('content_editable_state', true);
24660 			}
24661 
24662 			body.disabled = false;
24663 
24664 			/**
24665 			 * Schema instance, enables you to validate elements and it's children.
24666 			 *
24667 			 * @property schema
24668 			 * @type tinymce.html.Schema
24669 			 */
24670 			self.schema = new Schema(settings);
24671 
24672 			/**
24673 			 * DOM instance for the editor.
24674 			 *
24675 			 * @property dom
24676 			 * @type tinymce.dom.DOMUtils
24677 			 * @example
24678 			 * // Adds a class to all paragraphs within the editor
24679 			 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
24680 			 */
24681 			self.dom = new DOMUtils(doc, {
24682 				keep_values: true,
24683 				url_converter: self.convertURL,
24684 				url_converter_scope: self,
24685 				hex_colors: settings.force_hex_style_colors,
24686 				class_filter: settings.class_filter,
24687 				update_styles: true,
24688 				root_element: settings.content_editable ? self.id : null,
24689 				collect: settings.content_editable,
24690 				schema: self.schema,
24691 				onSetAttrib: function(e) {
24692 					self.fire('SetAttrib', e);
24693 				}
24694 			});
24695 
24696 			/**
24697 			 * HTML parser will be used when contents is inserted into the editor.
24698 			 *
24699 			 * @property parser
24700 			 * @type tinymce.html.DomParser
24701 			 */
24702 			self.parser = new DomParser(settings, self.schema);
24703 
24704 			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
24705 			self.parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) {
24706 				var i = nodes.length, node, dom = self.dom, value, internalName;
24707 
24708 				while (i--) {
24709 					node = nodes[i];
24710 					value = node.attr(name);
24711 					internalName = 'data-mce-' + name;
24712 
24713 					// Add internal attribute if we need to we don't on a refresh of the document
24714 					if (!node.attributes.map[internalName]) {
24715 						if (name === "style") {
24716 							node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
24717 						} else if (name === "tabindex") {
24718 							node.attr(internalName, value);
24719 							node.attr(name, null);
24720 						} else {
24721 							node.attr(internalName, self.convertURL(value, name, node.name));
24722 						}
24723 					}
24724 				}
24725 			});
24726 
24727 			// Keep scripts from executing
24728 			self.parser.addNodeFilter('script', function(nodes) {
24729 				var i = nodes.length, node;
24730 
24731 				while (i--) {
24732 					node = nodes[i];
24733 					node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
24734 				}
24735 			});
24736 
24737 			self.parser.addNodeFilter('#cdata', function(nodes) {
24738 				var i = nodes.length, node;
24739 
24740 				while (i--) {
24741 					node = nodes[i];
24742 					node.type = 8;
24743 					node.name = '#comment';
24744 					node.value = '[CDATA[' + node.value + ']]';
24745 				}
24746 			});
24747 
24748 			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
24749 				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
24750 
24751 				while (i--) {
24752 					node = nodes[i];
24753 
24754 					if (node.isEmpty(nonEmptyElements)) {
24755 						node.empty().append(new Node('br', 1)).shortEnded = true;
24756 					}
24757 				}
24758 			});
24759 
24760 			/**
24761 			 * DOM serializer for the editor. Will be used when contents is extracted from the editor.
24762 			 *
24763 			 * @property serializer
24764 			 * @type tinymce.dom.Serializer
24765 			 * @example
24766 			 * // Serializes the first paragraph in the editor into a string
24767 			 * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
24768 			 */
24769 			self.serializer = new DomSerializer(settings, self);
24770 
24771 			/**
24772 			 * Selection instance for the editor.
24773 			 *
24774 			 * @property selection
24775 			 * @type tinymce.dom.Selection
24776 			 * @example
24777 			 * // Sets some contents to the current selection in the editor
24778 			 * tinymce.activeEditor.selection.setContent('Some contents');
24779 			 *
24780 			 * // Gets the current selection
24781 			 * alert(tinymce.activeEditor.selection.getContent());
24782 			 *
24783 			 * // Selects the first paragraph found
24784 			 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
24785 			 */
24786 			self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
24787 
24788 			/**
24789 			 * Formatter instance.
24790 			 *
24791 			 * @property formatter
24792 			 * @type tinymce.Formatter
24793 			 */
24794 			self.formatter = new Formatter(self);
24795 
24796 			/**
24797 			 * Undo manager instance, responsible for handling undo levels.
24798 			 *
24799 			 * @property undoManager
24800 			 * @type tinymce.UndoManager
24801 			 * @example
24802 			 * // Undoes the last modification to the editor
24803 			 * tinymce.activeEditor.undoManager.undo();
24804 			 */
24805 			self.undoManager = new UndoManager(self);
24806 
24807 			self.forceBlocks = new ForceBlocks(self);
24808 			self.enterKey = new EnterKey(self);
24809 			self.editorCommands = new EditorCommands(self);
24810 
24811 			self.fire('PreInit');
24812 
24813 			if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
24814 				doc.body.spellcheck = false; // Gecko
24815 				DOM.setAttrib(body, "spellcheck", "false");
24816 			}
24817 
24818 			self.fire('PostRender');
24819 
24820 			self.quirks = Quirks(self);
24821 
24822 			if (settings.directionality) {
24823 				body.dir = settings.directionality;
24824 			}
24825 
24826 			if (settings.nowrap) {
24827 				body.style.whiteSpace = "nowrap";
24828 			}
24829 
24830 			if (settings.protect) {
24831 				self.on('BeforeSetContent', function(e) {
24832 					each(settings.protect, function(pattern) {
24833 						e.content = e.content.replace(pattern, function(str) {
24834 							return '<!--mce:protected ' + escape(str) + '-->';
24835 						});
24836 					});
24837 				});
24838 			}
24839 
24840 			self.on('SetContent', function() {
24841 				self.addVisual(self.getBody());
24842 			});
24843 
24844 			// Remove empty contents
24845 			if (settings.padd_empty_editor) {
24846 				self.on('PostProcess', function(e) {
24847 					e.content = e.content.replace(/^(<p[^>]*>( | |\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
24848 				});
24849 			}
24850 
24851 			self.load({initial: true, format: 'html'});
24852 			self.startContent = self.getContent({format: 'raw'});
24853 
24854 			/**
24855 			 * Is set to true after the editor instance has been initialized
24856 			 *
24857 			 * @property initialized
24858 			 * @type Boolean
24859 			 * @example
24860 			 * function isEditorInitialized(editor) {
24861 			 *     return editor && editor.initialized;
24862 			 * }
24863 			 */
24864 			self.initialized = true;
24865 			self.bindPendingEventDelegates();
24866 
24867 			self.fire('init');
24868 			self.focus(true);
24869 			self.nodeChanged({initial: true});
24870 			self.execCallback('init_instance_callback', self);
24871 
24872 			// Add editor specific CSS styles
24873 			if (self.contentStyles.length > 0) {
24874 				contentCssText = '';
24875 
24876 				each(self.contentStyles, function(style) {
24877 					contentCssText += style + "\r\n";
24878 				});
24879 
24880 				self.dom.addStyle(contentCssText);
24881 			}
24882 
24883 			// Load specified content CSS last
24884 			each(self.contentCSS, function(cssUrl) {
24885 				if (!self.loadedCSS[cssUrl]) {
24886 					self.dom.loadCSS(cssUrl);
24887 					self.loadedCSS[cssUrl] = true;
24888 				}
24889 			});
24890 
24891 			// Handle auto focus
24892 			if (settings.auto_focus) {
24893 				setTimeout(function () {
24894 					var ed = self.editorManager.get(settings.auto_focus);
24895 
24896 					ed.selection.select(ed.getBody(), 1);
24897 					ed.selection.collapse(1);
24898 					ed.getBody().focus();
24899 					ed.getWin().focus();
24900 				}, 100);
24901 			}
24902 
24903 			// Clean up references for IE
24904 			targetElm = doc = body = null;
24905 		},
24906 
24907 		/**
24908 		 * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
24909 		 * it will also place DOM focus inside the editor.
24910 		 *
24911 		 * @method focus
24912 		 * @param {Boolean} skip_focus Skip DOM focus. Just set is as the active editor.
24913 		 */
24914 		focus: function(skip_focus) {
24915 			var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
24916 			var controlElm, doc = self.getDoc(), body;
24917 
24918 			if (!skip_focus) {
24919 				// Get selected control element
24920 				rng = selection.getRng();
24921 				if (rng.item) {
24922 					controlElm = rng.item(0);
24923 				}
24924 
24925 				self._refreshContentEditable();
24926 
24927 				// Focus the window iframe
24928 				if (!contentEditable) {
24929 					// WebKit needs this call to fire focusin event properly see #5948
24930 					// But Opera pre Blink engine will produce an empty selection so skip Opera
24931 					if (!Env.opera) {
24932 						self.getBody().focus();
24933 					}
24934 
24935 					self.getWin().focus();
24936 				}
24937 
24938 				// Focus the body as well since it's contentEditable
24939 				if (isGecko || contentEditable) {
24940 					body = self.getBody();
24941 
24942 					// Check for setActive since it doesn't scroll to the element
24943 					if (body.setActive) {
24944 						// IE 11 sometimes throws "Invalid function" then fallback to focus
24945 						try {
24946 							body.setActive();
24947 						} catch (ex) {
24948 							body.focus();
24949 						}
24950 					} else {
24951 						body.focus();
24952 					}
24953 
24954 					if (contentEditable) {
24955 						selection.normalize();
24956 					}
24957 				}
24958 
24959 				// Restore selected control element
24960 				// This is needed when for example an image is selected within a
24961 				// layer a call to focus will then remove the control selection
24962 				if (controlElm && controlElm.ownerDocument == doc) {
24963 					rng = doc.body.createControlRange();
24964 					rng.addElement(controlElm);
24965 					rng.select();
24966 				}
24967 			}
24968 
24969 			if (self.editorManager.activeEditor != self) {
24970 				if ((oed = self.editorManager.activeEditor)) {
24971 					oed.fire('deactivate', {relatedTarget: self});
24972 				}
24973 
24974 				self.fire('activate', {relatedTarget: oed});
24975 			}
24976 
24977 			self.editorManager.activeEditor = self;
24978 		},
24979 
24980 		/**
24981 		 * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
24982 		 * There new event model is a better way to add callback so this method might be removed in the future.
24983 		 *
24984 		 * @method execCallback
24985 		 * @param {String} name Name of the callback to execute.
24986 		 * @return {Object} Return value passed from callback function.
24987 		 */
24988 		execCallback: function(name) {
24989 			var self = this, callback = self.settings[name], scope;
24990 
24991 			if (!callback) {
24992 				return;
24993 			}
24994 
24995 			// Look through lookup
24996 			if (self.callbackLookup && (scope = self.callbackLookup[name])) {
24997 				callback = scope.func;
24998 				scope = scope.scope;
24999 			}
25000 
25001 			if (typeof(callback) === 'string') {
25002 				scope = callback.replace(/\.\w+$/, '');
25003 				scope = scope ? resolve(scope) : 0;
25004 				callback = resolve(callback);
25005 				self.callbackLookup = self.callbackLookup || {};
25006 				self.callbackLookup[name] = {func: callback, scope: scope};
25007 			}
25008 
25009 			return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
25010 		},
25011 
25012 		/**
25013 		 * Translates the specified string by replacing variables with language pack items it will also check if there is
25014 		 * a key mathcin the input.
25015 		 *
25016 		 * @method translate
25017 		 * @param {String} text String to translate by the language pack data.
25018 		 * @return {String} Translated string.
25019 		 */
25020 		translate: function(text) {
25021 			var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
25022 
25023 			if (!text) {
25024 				return '';
25025 			}
25026 
25027 			return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
25028 				return i18n.data[lang + '.' + b] || '{#' + b + '}';
25029 			});
25030 		},
25031 
25032 		/**
25033 		 * Returns a language pack item by name/key.
25034 		 *
25035 		 * @method getLang
25036 		 * @param {String} name Name/key to get from the language pack.
25037 		 * @param {String} defaultVal Optional default value to retrive.
25038 		 */
25039 		getLang: function(name, defaultVal) {
25040 			return (
25041 				this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
25042 				(defaultVal !== undefined ? defaultVal : '{#' + name + '}')
25043 			);
25044 		},
25045 
25046 		/**
25047 		 * Returns a configuration parameter by name.
25048 		 *
25049 		 * @method getParam
25050 		 * @param {String} name Configruation parameter to retrive.
25051 		 * @param {String} defaultVal Optional default value to return.
25052 		 * @param {String} type Optional type parameter.
25053 		 * @return {String} Configuration parameter value or default value.
25054 		 * @example
25055 		 * // Returns a specific config value from the currently active editor
25056 		 * var someval = tinymce.activeEditor.getParam('myvalue');
25057 		 *
25058 		 * // Returns a specific config value from a specific editor instance by id
25059 		 * var someval2 = tinymce.get('my_editor').getParam('myvalue');
25060 		 */
25061 		getParam: function(name, defaultVal, type) {
25062 			var value = name in this.settings ? this.settings[name] : defaultVal, output;
25063 
25064 			if (type === 'hash') {
25065 				output = {};
25066 
25067 				if (typeof(value) === 'string') {
25068 					each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
25069 						value = value.split('=');
25070 
25071 						if (value.length > 1) {
25072 							output[trim(value[0])] = trim(value[1]);
25073 						} else {
25074 							output[trim(value[0])] = trim(value);
25075 						}
25076 					});
25077 				} else {
25078 					output = value;
25079 				}
25080 
25081 				return output;
25082 			}
25083 
25084 			return value;
25085 		},
25086 
25087 		/**
25088 		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
25089 		 * need to update the UI states or element path etc.
25090 		 *
25091 		 * @method nodeChanged
25092 		 */
25093 		nodeChanged: function() {
25094 			var self = this, selection = self.selection, node, parents, root;
25095 
25096 			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
25097 			if (self.initialized && !self.settings.disable_nodechange && !self.settings.readonly) {
25098 				// Get start node
25099 				root = self.getBody();
25100 				node = selection.getStart() || root;
25101 				node = ie && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
25102 
25103 				// Edge case for <p>|<img></p>
25104 				if (node.nodeName == 'IMG' && selection.isCollapsed()) {
25105 					node = node.parentNode;
25106 				}
25107 
25108 				// Get parents and add them to object
25109 				parents = [];
25110 				self.dom.getParent(node, function(node) {
25111 					if (node === root) {
25112 						return true;
25113 					}
25114 
25115 					parents.push(node);
25116 				});
25117 
25118 				self.fire('NodeChange', {element: node, parents: parents});
25119 			}
25120 		},
25121 
25122 		/**
25123 		 * Adds a button that later gets created by the theme in the editors toolbars.
25124 		 *
25125 		 * @method addButton
25126 		 * @param {String} name Button name to add.
25127 		 * @param {Object} settings Settings object with title, cmd etc.
25128 		 * @example
25129 		 * // Adds a custom button to the editor that inserts contents when clicked
25130 		 * tinymce.init({
25131 		 *    ...
25132 		 *
25133 		 *    toolbar: 'example'
25134 		 *
25135 		 *    setup: function(ed) {
25136 		 *       ed.addButton('example', {
25137 		 *          title: 'My title',
25138 		 *          image: '../js/tinymce/plugins/example/img/example.gif',
25139 		 *          onclick: function() {
25140 		 *             ed.insertContent('Hello world!!');
25141 		 *          }
25142 		 *       });
25143 		 *    }
25144 		 * });
25145 		 */
25146 		addButton: function(name, settings) {
25147 			var self = this;
25148 
25149 			if (settings.cmd) {
25150 				settings.onclick = function() {
25151 					self.execCommand(settings.cmd);
25152 				};
25153 			}
25154 
25155 			if (!settings.text && !settings.icon) {
25156 				settings.icon = name;
25157 			}
25158 
25159 			self.buttons = self.buttons || {};
25160 			settings.tooltip = settings.tooltip || settings.title;
25161 			self.buttons[name] = settings;
25162 		},
25163 
25164 		/**
25165 		 * Adds a menu item to be used in the menus of the theme. There might be multiple instances
25166 		 * of this menu item for example it might be used in the main menus of the theme but also in
25167 		 * the context menu so make sure that it's self contained and supports multiple instances.
25168 		 *
25169 		 * @method addMenuItem
25170 		 * @param {String} name Menu item name to add.
25171 		 * @param {Object} settings Settings object with title, cmd etc.
25172 		 * @example
25173 		 * // Adds a custom menu item to the editor that inserts contents when clicked
25174 		 * // The context option allows you to add the menu item to an existing default menu
25175 		 * tinymce.init({
25176 		 *    ...
25177 		 *
25178 		 *    setup: function(ed) {
25179 		 *       ed.addMenuItem('example', {
25180 		 *          text: 'My menu item',
25181 		 *          context: 'tools',
25182 		 *          onclick: function() {
25183 		 *             ed.insertContent('Hello world!!');
25184 		 *          }
25185 		 *       });
25186 		 *    }
25187 		 * });
25188 		 */
25189 		addMenuItem: function(name, settings) {
25190 			var self = this;
25191 
25192 			if (settings.cmd) {
25193 				settings.onclick = function() {
25194 					self.execCommand(settings.cmd);
25195 				};
25196 			}
25197 
25198 			self.menuItems = self.menuItems || {};
25199 			self.menuItems[name] = settings;
25200 		},
25201 
25202 		/**
25203 		 * Adds a custom command to the editor, you can also override existing commands with this method.
25204 		 * The command that you add can be executed with execCommand.
25205 		 *
25206 		 * @method addCommand
25207 		 * @param {String} name Command name to add/override.
25208 		 * @param {addCommandCallback} callback Function to execute when the command occurs.
25209 		 * @param {Object} scope Optional scope to execute the function in.
25210 		 * @example
25211 		 * // Adds a custom command that later can be executed using execCommand
25212 		 * tinymce.init({
25213 		 *    ...
25214 		 *
25215 		 *    setup: function(ed) {
25216 		 *       // Register example command
25217 		 *       ed.addCommand('mycommand', function(ui, v) {
25218 		 *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
25219 		 *       });
25220 		 *    }
25221 		 * });
25222 		 */
25223 		addCommand: function(name, callback, scope) {
25224 			/**
25225 			 * Callback function that gets called when a command is executed.
25226 			 *
25227 			 * @callback addCommandCallback
25228 			 * @param {Boolean} ui Display UI state true/false.
25229 			 * @param {Object} value Optional value for command.
25230 			 * @return {Boolean} True/false state if the command was handled or not.
25231 			 */
25232 			this.execCommands[name] = {func: callback, scope: scope || this};
25233 		},
25234 
25235 		/**
25236 		 * Adds a custom query state command to the editor, you can also override existing commands with this method.
25237 		 * The command that you add can be executed with queryCommandState function.
25238 		 *
25239 		 * @method addQueryStateHandler
25240 		 * @param {String} name Command name to add/override.
25241 		 * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
25242 		 * @param {Object} scope Optional scope to execute the function in.
25243 		 */
25244 		addQueryStateHandler: function(name, callback, scope) {
25245 			/**
25246 			 * Callback function that gets called when a queryCommandState is executed.
25247 			 *
25248 			 * @callback addQueryStateHandlerCallback
25249 			 * @return {Boolean} True/false state if the command is enabled or not like is it bold.
25250 			 */
25251 			this.queryStateCommands[name] = {func: callback, scope: scope || this};
25252 		},
25253 
25254 		/**
25255 		 * Adds a custom query value command to the editor, you can also override existing commands with this method.
25256 		 * The command that you add can be executed with queryCommandValue function.
25257 		 *
25258 		 * @method addQueryValueHandler
25259 		 * @param {String} name Command name to add/override.
25260 		 * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
25261 		 * @param {Object} scope Optional scope to execute the function in.
25262 		 */
25263 		addQueryValueHandler: function(name, callback, scope) {
25264 			/**
25265 			 * Callback function that gets called when a queryCommandValue is executed.
25266 			 *
25267 			 * @callback addQueryValueHandlerCallback
25268 			 * @return {Object} Value of the command or undefined.
25269 			 */
25270 			this.queryValueCommands[name] = {func: callback, scope: scope || this};
25271 		},
25272 
25273 		/**
25274 		 * Adds a keyboard shortcut for some command or function.
25275 		 *
25276 		 * @method addShortcut
25277 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
25278 		 * @param {String} desc Text description for the command.
25279 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
25280 		 * @param {Object} sc Optional scope to execute the function in.
25281 		 * @return {Boolean} true/false state if the shortcut was added or not.
25282 		 */
25283 		addShortcut: function(pattern, desc, cmdFunc, scope) {
25284 			this.shortcuts.add(pattern, desc, cmdFunc, scope);
25285 		},
25286 
25287 		/**
25288 		 * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
25289 		 * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
25290 		 * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
25291 		 * return true it will handle the command as a internal browser command.
25292 		 *
25293 		 * @method execCommand
25294 		 * @param {String} cmd Command name to execute, for example mceLink or Bold.
25295 		 * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
25296 		 * @param {mixed} value Optional command value, this can be anything.
25297 		 * @param {Object} a Optional arguments object.
25298 		 */
25299 		execCommand: function(cmd, ui, value, args) {
25300 			var self = this, state = 0, cmdItem;
25301 
25302 			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(cmd) && (!args || !args.skip_focus)) {
25303 				self.focus();
25304 			}
25305 
25306 			args = extend({}, args);
25307 			args = self.fire('BeforeExecCommand', {command: cmd, ui: ui, value: value});
25308 			if (args.isDefaultPrevented()) {
25309 				return false;
25310 			}
25311 
25312 			// Registred commands
25313 			if ((cmdItem = self.execCommands[cmd])) {
25314 				// Fall through on true
25315 				if (cmdItem.func.call(cmdItem.scope, ui, value) !== true) {
25316 					self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
25317 					return true;
25318 				}
25319 			}
25320 
25321 			// Plugin commands
25322 			each(self.plugins, function(p) {
25323 				if (p.execCommand && p.execCommand(cmd, ui, value)) {
25324 					self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
25325 					state = true;
25326 					return false;
25327 				}
25328 			});
25329 
25330 			if (state) {
25331 				return state;
25332 			}
25333 
25334 			// Theme commands
25335 			if (self.theme && self.theme.execCommand && self.theme.execCommand(cmd, ui, value)) {
25336 				self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
25337 				return true;
25338 			}
25339 
25340 			// Editor commands
25341 			if (self.editorCommands.execCommand(cmd, ui, value)) {
25342 				self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
25343 				return true;
25344 			}
25345 
25346 			// Browser commands
25347 			self.getDoc().execCommand(cmd, ui, value);
25348 			self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
25349 		},
25350 
25351 		/**
25352 		 * Returns a command specific state, for example if bold is enabled or not.
25353 		 *
25354 		 * @method queryCommandState
25355 		 * @param {string} cmd Command to query state from.
25356 		 * @return {Boolean} Command specific state, for example if bold is enabled or not.
25357 		 */
25358 		queryCommandState: function(cmd) {
25359 			var self = this, queryItem, returnVal;
25360 
25361 			// Is hidden then return undefined
25362 			if (self._isHidden()) {
25363 				return;
25364 			}
25365 
25366 			// Registred commands
25367 			if ((queryItem = self.queryStateCommands[cmd])) {
25368 				returnVal = queryItem.func.call(queryItem.scope);
25369 
25370 				// Fall though on true
25371 				if (returnVal !== true) {
25372 					return returnVal;
25373 				}
25374 			}
25375 
25376 			// Editor commands
25377 			returnVal = self.editorCommands.queryCommandState(cmd);
25378 			if (returnVal !== -1) {
25379 				return returnVal;
25380 			}
25381 
25382 			// Browser commands
25383 			try {
25384 				return self.getDoc().queryCommandState(cmd);
25385 			} catch (ex) {
25386 				// Fails sometimes see bug: 1896577
25387 			}
25388 		},
25389 
25390 		/**
25391 		 * Returns a command specific value, for example the current font size.
25392 		 *
25393 		 * @method queryCommandValue
25394 		 * @param {string} cmd Command to query value from.
25395 		 * @return {Object} Command specific value, for example the current font size.
25396 		 */
25397 		queryCommandValue: function(cmd) {
25398 			var self = this, queryItem, returnVal;
25399 
25400 			// Is hidden then return undefined
25401 			if (self._isHidden()) {
25402 				return;
25403 			}
25404 
25405 			// Registred commands
25406 			if ((queryItem = self.queryValueCommands[cmd])) {
25407 				returnVal = queryItem.func.call(queryItem.scope);
25408 
25409 				// Fall though on true
25410 				if (returnVal !== true) {
25411 					return returnVal;
25412 				}
25413 			}
25414 
25415 			// Editor commands
25416 			returnVal = self.editorCommands.queryCommandValue(cmd);
25417 			if (returnVal !== undefined) {
25418 				return returnVal;
25419 			}
25420 
25421 			// Browser commands
25422 			try {
25423 				return self.getDoc().queryCommandValue(cmd);
25424 			} catch (ex) {
25425 				// Fails sometimes see bug: 1896577
25426 			}
25427 		},
25428 
25429 		/**
25430 		 * Shows the editor and hides any textarea/div that the editor is supposed to replace.
25431 		 *
25432 		 * @method show
25433 		 */
25434 		show: function() {
25435 			var self = this;
25436 
25437 			if (self.hidden) {
25438 				self.hidden = false;
25439 
25440 				if (self.inline) {
25441 					self.getBody().contentEditable = true;
25442 				} else {
25443 					DOM.show(self.getContainer());
25444 					DOM.hide(self.id);
25445 				}
25446 
25447 				self.load();
25448 				self.fire('show');
25449 			}
25450 		},
25451 
25452 		/**
25453 		 * Hides the editor and shows any textarea/div that the editor is supposed to replace.
25454 		 *
25455 		 * @method hide
25456 		 */
25457 		hide: function() {
25458 			var self = this, doc = self.getDoc();
25459 
25460 			if (!self.hidden) {
25461 				self.hidden = true;
25462 
25463 				// Fixed bug where IE has a blinking cursor left from the editor
25464 				if (ie && doc && !self.inline) {
25465 					doc.execCommand('SelectAll');
25466 				}
25467 
25468 				// We must save before we hide so Safari doesn't crash
25469 				self.save();
25470 
25471 				if (self.inline) {
25472 					self.getBody().contentEditable = false;
25473 
25474 					// Make sure the editor gets blurred
25475 					if (self == self.editorManager.focusedEditor) {
25476 						self.editorManager.focusedEditor = null;
25477 					}
25478 				} else {
25479 					DOM.hide(self.getContainer());
25480 					DOM.setStyle(self.id, 'display', self.orgDisplay);
25481 				}
25482 
25483 				self.fire('hide');
25484 			}
25485 		},
25486 
25487 		/**
25488 		 * Returns true/false if the editor is hidden or not.
25489 		 *
25490 		 * @method isHidden
25491 		 * @return {Boolean} True/false if the editor is hidden or not.
25492 		 */
25493 		isHidden: function() {
25494 			return !!this.hidden;
25495 		},
25496 
25497 		/**
25498 		 * Sets the progress state, this will display a throbber/progess for the editor.
25499 		 * This is ideal for asycronous operations like an AJAX save call.
25500 		 *
25501 		 * @method setProgressState
25502 		 * @param {Boolean} state Boolean state if the progress should be shown or hidden.
25503 		 * @param {Number} time Optional time to wait before the progress gets shown.
25504 		 * @return {Boolean} Same as the input state.
25505 		 * @example
25506 		 * // Show progress for the active editor
25507 		 * tinymce.activeEditor.setProgressState(true);
25508 		 * 
25509 		 * // Hide progress for the active editor
25510 		 * tinymce.activeEditor.setProgressState(false);
25511 		 * 
25512 		 * // Show progress after 3 seconds
25513 		 * tinymce.activeEditor.setProgressState(true, 3000);
25514 		 */
25515 		setProgressState: function(state, time) {
25516 			this.fire('ProgressState', {state: state, time: time});
25517 		},
25518 
25519 		/**
25520 		 * Loads contents from the textarea or div element that got converted into an editor instance.
25521 		 * This method will move the contents from that textarea or div into the editor by using setContent
25522 		 * so all events etc that method has will get dispatched as well.
25523 		 *
25524 		 * @method load
25525 		 * @param {Object} args Optional content object, this gets passed around through the whole load process.
25526 		 * @return {String} HTML string that got set into the editor.
25527 		 */
25528 		load: function(args) {
25529 			var self = this, elm = self.getElement(), html;
25530 
25531 			if (elm) {
25532 				args = args || {};
25533 				args.load = true;
25534 
25535 				html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
25536 				args.element = elm;
25537 
25538 				if (!args.no_events) {
25539 					self.fire('LoadContent', args);
25540 				}
25541 
25542 				args.element = elm = null;
25543 
25544 				return html;
25545 			}
25546 		},
25547 
25548 		/**
25549 		 * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
25550 		 * This method will move the HTML contents from the editor into that textarea or div by getContent
25551 		 * so all events etc that method has will get dispatched as well.
25552 		 *
25553 		 * @method save
25554 		 * @param {Object} args Optional content object, this gets passed around through the whole save process.
25555 		 * @return {String} HTML string that got set into the textarea/div.
25556 		 */
25557 		save: function(args) {
25558 			var self = this, elm = self.getElement(), html, form;
25559 
25560 			if (!elm || !self.initialized) {
25561 				return;
25562 			}
25563 
25564 			args = args || {};
25565 			args.save = true;
25566 
25567 			args.element = elm;
25568 			html = args.content = self.getContent(args);
25569 
25570 			if (!args.no_events) {
25571 				self.fire('SaveContent', args);
25572 			}
25573 
25574 			html = args.content;
25575 
25576 			if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
25577 				// Update DIV element when not in inline mode
25578 				if (!self.inline) {
25579 					elm.innerHTML = html;
25580 				}
25581 
25582 				// Update hidden form element
25583 				if ((form = DOM.getParent(self.id, 'form'))) {
25584 					each(form.elements, function(elm) {
25585 						if (elm.name == self.id) {
25586 							elm.value = html;
25587 							return false;
25588 						}
25589 					});
25590 				}
25591 			} else {
25592 				elm.value = html;
25593 			}
25594 
25595 			args.element = elm = null;
25596 
25597 			if (args.set_dirty !== false) {
25598 				self.isNotDirty = true;
25599 			}
25600 
25601 			return html;
25602 		},
25603 
25604 		/**
25605 		 * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
25606 		 * the different cleanup rules options.
25607 		 *
25608 		 * @method setContent
25609 		 * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
25610 		 * @param {Object} args Optional content object, this gets passed around through the whole set process.
25611 		 * @return {String} HTML string that got set into the editor.
25612 		 * @example
25613 		 * // Sets the HTML contents of the activeEditor editor
25614 		 * tinymce.activeEditor.setContent('<span>some</span> html');
25615 		 *
25616 		 * // Sets the raw contents of the activeEditor editor
25617 		 * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'});
25618 		 *
25619 		 * // Sets the content of a specific editor (my_editor in this example)
25620 		 * tinymce.get('my_editor').setContent(data);
25621 		 *
25622 		 * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
25623 		 * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
25624 		 */
25625 		setContent: function(content, args) {
25626 			var self = this, body = self.getBody(), forcedRootBlockName;
25627 
25628 			// Setup args object
25629 			args = args || {};
25630 			args.format = args.format || 'html';
25631 			args.set = true;
25632 			args.content = content;
25633 
25634 			// Do preprocessing
25635 			if (!args.no_events) {
25636 				self.fire('BeforeSetContent', args);
25637 			}
25638 
25639 			content = args.content;
25640 
25641 			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
25642 			// It will also be impossible to place the caret in the editor unless there is a BR element present
25643 			if (content.length === 0 || /^\s+$/.test(content)) {
25644 				forcedRootBlockName = self.settings.forced_root_block;
25645 
25646 				// Check if forcedRootBlock is configured and that the block is a valid child of the body
25647 				if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
25648 					// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
25649 					content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
25650 					content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
25651 				} else if (!ie) {
25652 					// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
25653 					content = '<br data-mce-bogus="1">';
25654 				}
25655 
25656 				body.innerHTML = content;
25657 
25658 				self.fire('SetContent', args);
25659 			} else {
25660 				// Parse and serialize the html
25661 				if (args.format !== 'raw') {
25662 					content = new Serializer({}, self.schema).serialize(
25663 						self.parser.parse(content, {isRootContent: true})
25664 					);
25665 				}
25666 
25667 				// Set the new cleaned contents to the editor
25668 				args.content = trim(content);
25669 				self.dom.setHTML(body, args.content);
25670 
25671 				// Do post processing
25672 				if (!args.no_events) {
25673 					self.fire('SetContent', args);
25674 				}
25675 
25676 				// Don't normalize selection if the focused element isn't the body in
25677 				// content editable mode since it will steal focus otherwise
25678 				/*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
25679 					self.selection.normalize();
25680 				}*/
25681 			}
25682 
25683 			return args.content;
25684 		},
25685 
25686 		/**
25687 		 * Gets the content from the editor instance, this will cleanup the content before it gets returned using
25688 		 * the different cleanup rules options.
25689 		 *
25690 		 * @method getContent
25691 		 * @param {Object} args Optional content object, this gets passed around through the whole get process.
25692 		 * @return {String} Cleaned content string, normally HTML contents.
25693 		 * @example
25694 		 * // Get the HTML contents of the currently active editor
25695 		 * console.debug(tinymce.activeEditor.getContent());
25696 		 *
25697 		 * // Get the raw contents of the currently active editor
25698 		 * tinymce.activeEditor.getContent({format: 'raw'});
25699 		 *
25700 		 * // Get content of a specific editor:
25701 		 * tinymce.get('content id').getContent()
25702 		 */
25703 		getContent: function(args) {
25704 			var self = this, content, body = self.getBody();
25705 
25706 			// Setup args object
25707 			args = args || {};
25708 			args.format = args.format || 'html';
25709 			args.get = true;
25710 			args.getInner = true;
25711 
25712 			// Do preprocessing
25713 			if (!args.no_events) {
25714 				self.fire('BeforeGetContent', args);
25715 			}
25716 
25717 			// Get raw contents or by default the cleaned contents
25718 			if (args.format == 'raw') {
25719 				content = body.innerHTML;
25720 			} else if (args.format == 'text') {
25721 				content = body.innerText || body.textContent;
25722 			} else {
25723 				content = self.serializer.serialize(body, args);
25724 			}
25725 
25726 			// Trim whitespace in beginning/end of HTML
25727 			if (args.format != 'text') {
25728 				args.content = trim(content);
25729 			} else {
25730 				args.content = content;
25731 			}
25732 
25733 			// Do post processing
25734 			if (!args.no_events) {
25735 				self.fire('GetContent', args);
25736 			}
25737 
25738 			return args.content;
25739 		},
25740 
25741 		/**
25742 		 * Inserts content at caret position.
25743 		 *
25744 		 * @method insertContent
25745 		 * @param {String} content Content to insert.
25746 		 */
25747 		insertContent: function(content) {
25748 			this.execCommand('mceInsertContent', false, content);
25749 		},
25750 
25751 		/**
25752 		 * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
25753 		 *
25754 		 * @method isDirty
25755 		 * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
25756 		 * @example
25757 		 * if (tinymce.activeEditor.isDirty())
25758 		 *     alert("You must save your contents.");
25759 		 */
25760 		isDirty: function() {
25761 			return !this.isNotDirty;
25762 		},
25763 
25764 		/**
25765 		 * Returns the editors container element. The container element wrappes in
25766 		 * all the elements added to the page for the editor. Such as UI, iframe etc.
25767 		 *
25768 		 * @method getContainer
25769 		 * @return {Element} HTML DOM element for the editor container.
25770 		 */
25771 		getContainer: function() {
25772 			var self = this;
25773 
25774 			if (!self.container) {
25775 				self.container = DOM.get(self.editorContainer || self.id + '_parent');
25776 			}
25777 
25778 			return self.container;
25779 		},
25780 
25781 		/**
25782 		 * Returns the editors content area container element. The this element is the one who
25783 		 * holds the iframe or the editable element.
25784 		 *
25785 		 * @method getContentAreaContainer
25786 		 * @return {Element} HTML DOM element for the editor area container.
25787 		 */
25788 		getContentAreaContainer: function() {
25789 			return this.contentAreaContainer;
25790 		},
25791 
25792 		/**
25793 		 * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
25794 		 *
25795 		 * @method getElement
25796 		 * @return {Element} HTML DOM element for the replaced element.
25797 		 */
25798 		getElement: function() {
25799 			return DOM.get(this.settings.content_element || this.id);
25800 		},
25801 
25802 		/**
25803 		 * Returns the iframes window object.
25804 		 *
25805 		 * @method getWin
25806 		 * @return {Window} Iframe DOM window object.
25807 		 */
25808 		getWin: function() {
25809 			var self = this, elm;
25810 
25811 			if (!self.contentWindow) {
25812 				elm = DOM.get(self.id + "_ifr");
25813 
25814 				if (elm) {
25815 					self.contentWindow = elm.contentWindow;
25816 				}
25817 			}
25818 
25819 			return self.contentWindow;
25820 		},
25821 
25822 		/**
25823 		 * Returns the iframes document object.
25824 		 *
25825 		 * @method getDoc
25826 		 * @return {Document} Iframe DOM document object.
25827 		 */
25828 		getDoc: function() {
25829 			var self = this, win;
25830 
25831 			if (!self.contentDocument) {
25832 				win = self.getWin();
25833 
25834 				if (win) {
25835 					self.contentDocument = win.document;
25836 				}
25837 			}
25838 
25839 			return self.contentDocument;
25840 		},
25841 
25842 		/**
25843 		 * Returns the iframes body element.
25844 		 *
25845 		 * @method getBody
25846 		 * @return {Element} Iframe body element.
25847 		 */
25848 		getBody: function() {
25849 			return this.bodyElement || this.getDoc().body;
25850 		},
25851 
25852 		/**
25853 		 * URL converter function this gets executed each time a user adds an img, a or
25854 		 * any other element that has a URL in it. This will be called both by the DOM and HTML
25855 		 * manipulation functions.
25856 		 *
25857 		 * @method convertURL
25858 		 * @param {string} url URL to convert.
25859 		 * @param {string} name Attribute name src, href etc.
25860 		 * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
25861 		 * @return {string} Converted URL string.
25862 		 */
25863 		convertURL: function(url, name, elm) {
25864 			var self = this, settings = self.settings;
25865 
25866 			// Use callback instead
25867 			if (settings.urlconverter_callback) {
25868 				return self.execCallback('urlconverter_callback', url, elm, true, name);
25869 			}
25870 
25871 			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
25872 			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) {
25873 				return url;
25874 			}
25875 
25876 			// Convert to relative
25877 			if (settings.relative_urls) {
25878 				return self.documentBaseURI.toRelative(url);
25879 			}
25880 
25881 			// Convert to absolute
25882 			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
25883 
25884 			return url;
25885 		},
25886 
25887 		/**
25888 		 * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
25889 		 *
25890 		 * @method addVisual
25891 		 * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid.
25892 		 */
25893 		addVisual: function(elm) {
25894 			var self = this, settings = self.settings, dom = self.dom, cls;
25895 
25896 			elm = elm || self.getBody();
25897 
25898 			if (self.hasVisual === undefined) {
25899 				self.hasVisual = settings.visual;
25900 			}
25901 
25902 			each(dom.select('table,a', elm), function(elm) {
25903 				var value;
25904 
25905 				switch (elm.nodeName) {
25906 					case 'TABLE':
25907 						cls = settings.visual_table_class || 'mce-item-table';
25908 						value = dom.getAttrib(elm, 'border');
25909 
25910 						if (!value || value == '0') {
25911 							if (self.hasVisual) {
25912 								dom.addClass(elm, cls);
25913 							} else {
25914 								dom.removeClass(elm, cls);
25915 							}
25916 						}
25917 
25918 						return;
25919 
25920 					case 'A':
25921 						if (!dom.getAttrib(elm, 'href', false)) {
25922 							value = dom.getAttrib(elm, 'name') || elm.id;
25923 							cls = settings.visual_anchor_class || 'mce-item-anchor';
25924 
25925 							if (value) {
25926 								if (self.hasVisual) {
25927 									dom.addClass(elm, cls);
25928 								} else {
25929 									dom.removeClass(elm, cls);
25930 								}
25931 							}
25932 						}
25933 
25934 						return;
25935 				}
25936 			});
25937 
25938 			self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual});
25939 		},
25940 
25941 		/**
25942 		 * Removes the editor from the dom and tinymce collection.
25943 		 *
25944 		 * @method remove
25945 		 */
25946 		remove: function() {
25947 			var self = this;
25948 
25949 			if (!self.removed) {
25950 				self.removed = 1;
25951 				self.save();
25952 
25953 				// Remove any hidden input
25954 				if (self.hasHiddenInput) {
25955 					DOM.remove(self.getElement().nextSibling);
25956 				}
25957 
25958 				if (!self.inline) {
25959 					// IE 9 has a bug where the selection stops working if you place the
25960 					// caret inside the editor then remove the iframe
25961 					if (ie && ie < 10) {
25962 						self.getDoc().execCommand('SelectAll', false, null);
25963 					}
25964 
25965 					DOM.setStyle(self.id, 'display', self.orgDisplay);
25966 					self.getBody().onload = null; // Prevent #6816
25967 
25968 					// Don't clear the window or document if content editable
25969 					// is enabled since other instances might still be present
25970 					Event.unbind(self.getWin());
25971 					Event.unbind(self.getDoc());
25972 				}
25973 
25974 				var elm = self.getContainer();
25975 				Event.unbind(self.getBody());
25976 				Event.unbind(elm);
25977 
25978 				self.fire('remove');
25979 
25980 				self.editorManager.remove(self);
25981 				DOM.remove(elm);
25982 				self.destroy();
25983 			}
25984 		},
25985 
25986 		/**
25987 		 * Destroys the editor instance by removing all events, element references or other resources
25988 		 * that could leak memory. This method will be called automatically when the page is unloaded
25989 		 * but you can also call it directly if you know what you are doing.
25990 		 *
25991 		 * @method destroy
25992 		 * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
25993 		 */
25994 		destroy: function(automatic) {
25995 			var self = this, form;
25996 
25997 			// One time is enough
25998 			if (self.destroyed) {
25999 				return;
26000 			}
26001 
26002 			// If user manually calls destroy and not remove
26003 			// Users seems to have logic that calls destroy instead of remove
26004 			if (!automatic && !self.removed) {
26005 				self.remove();
26006 				return;
26007 			}
26008 
26009 			// We must unbind on Gecko since it would otherwise produce the pesky "attempt
26010 			// to run compile-and-go script on a cleared scope" message
26011 			if (automatic && isGecko) {
26012 				Event.unbind(self.getDoc());
26013 				Event.unbind(self.getWin());
26014 				Event.unbind(self.getBody());
26015 			}
26016 
26017 			if (!automatic) {
26018 				self.editorManager.off('beforeunload', self._beforeUnload);
26019 
26020 				// Manual destroy
26021 				if (self.theme && self.theme.destroy) {
26022 					self.theme.destroy();
26023 				}
26024 
26025 				// Destroy controls, selection and dom
26026 				self.selection.destroy();
26027 				self.dom.destroy();
26028 			}
26029 
26030 			form = self.formElement;
26031 			if (form) {
26032 				if (form._mceOldSubmit) {
26033 					form.submit = form._mceOldSubmit;
26034 					form._mceOldSubmit = null;
26035 				}
26036 
26037 				DOM.unbind(form, 'submit reset', self.formEventDelegate);
26038 			}
26039 
26040 			self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null;
26041 			self.settings.content_element = self.bodyElement = self.contentDocument = self.contentWindow = null;
26042 
26043 			if (self.selection) {
26044 				self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null;
26045 			}
26046 
26047 			self.destroyed = 1;
26048 		},
26049 
26050 		// Internal functions
26051 
26052 		_refreshContentEditable: function() {
26053 			var self = this, body, parent;
26054 
26055 			// Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
26056 			if (self._isHidden()) {
26057 				body = self.getBody();
26058 				parent = body.parentNode;
26059 
26060 				parent.removeChild(body);
26061 				parent.appendChild(body);
26062 
26063 				body.focus();
26064 			}
26065 		},
26066 
26067 		_isHidden: function() {
26068 			var sel;
26069 
26070 			if (!isGecko) {
26071 				return 0;
26072 			}
26073 
26074 			// Weird, wheres that cursor selection?
26075 			sel = this.selection.getSel();
26076 			return (!sel || !sel.rangeCount || sel.rangeCount === 0);
26077 		}
26078 	};
26079 
26080 	extend(Editor.prototype, EditorObservable);
26081 
26082 	return Editor;
26083 });
26084 
26085 // Included from: js/tinymce/classes/util/I18n.js
26086 
26087 /**
26088  * I18n.js
26089  *
26090  * Copyright, Moxiecode Systems AB
26091  * Released under LGPL License.
26092  *
26093  * License: http://www.tinymce.com/license
26094  * Contributing: http://www.tinymce.com/contributing
26095  */
26096 
26097 /**
26098  * I18n class that handles translation of TinyMCE UI.
26099  * Uses po style with csharp style parameters.
26100  *
26101  * @class tinymce.util.I18n
26102  */
26103 define("tinymce/util/I18n", [], function() {
26104 	"use strict";
26105 
26106 	var data = {};
26107 
26108 	return {
26109 		/**
26110 		 * Property gets set to true if a RTL language pack was loaded.
26111 		 *
26112 		 * @property rtl
26113 		 * @type Boolean
26114 		 */
26115 		rtl: false,
26116 
26117 		/**
26118 		 * Adds translations for a specific language code.
26119 		 *
26120 		 * @method add
26121 		 * @param {String} code Language code like sv_SE.
26122 		 * @param {Array} items Name/value array with English en_US to sv_SE.
26123 		 */
26124 		add: function(code, items) {
26125 			for (var name in items) {
26126 				data[name] = items[name];
26127 			}
26128 
26129 			this.rtl = this.rtl || data._dir === 'rtl';
26130 		},
26131 
26132 		/**
26133 		 * Translates the specified text.
26134 		 *
26135 		 * It has a few formats:
26136 		 * I18n.translate("Text");
26137 		 * I18n.translate(["Text {0}/{1}", 0, 1]);
26138 		 * I18n.translate({raw: "Raw string"});
26139 		 *
26140 		 * @method translate
26141 		 * @param {String/Object/Array} text Text to translate.
26142 		 * @return {String} String that got translated.
26143 		 */
26144 		translate: function(text) {
26145 			if (typeof(text) == "undefined") {
26146 				return text;
26147 			}
26148 
26149 			if (typeof(text) != "string" && text.raw) {
26150 				return text.raw;
26151 			}
26152 
26153 			if (text.push) {
26154 				var values = text.slice(1);
26155 
26156 				text = (data[text[0]] || text[0]).replace(/\{([^\}]+)\}/g, function(match1, match2) {
26157 					return values[match2];
26158 				});
26159 			}
26160 
26161 			return data[text] || text;
26162 		},
26163 
26164 		data: data
26165 	};
26166 });
26167 
26168 // Included from: js/tinymce/classes/FocusManager.js
26169 
26170 /**
26171  * FocusManager.js
26172  *
26173  * Copyright, Moxiecode Systems AB
26174  * Released under LGPL License.
26175  *
26176  * License: http://www.tinymce.com/license
26177  * Contributing: http://www.tinymce.com/contributing
26178  */
26179 
26180 /**
26181  * This class manages the focus/blur state of the editor. This class is needed since some
26182  * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar.
26183  *
26184  * This class will fire two events focus and blur on the editor instances that got affected.
26185  * It will also handle the restore of selection when the focus is lost and returned.
26186  *
26187  * @class tinymce.FocusManager
26188  */
26189 define("tinymce/FocusManager", [
26190 	"tinymce/dom/DOMUtils",
26191 	"tinymce/Env"
26192 ], function(DOMUtils, Env) {
26193 	var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM;
26194 
26195 	/**
26196 	 * Constructs a new focus manager instance.
26197 	 *
26198 	 * @constructor FocusManager
26199 	 * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for.
26200 	 */
26201 	function FocusManager(editorManager) {
26202 		function getActiveElement() {
26203 			try {
26204 				return document.activeElement;
26205 			} catch (ex) {
26206 				// IE sometimes fails to get the activeElement when resizing table
26207 				// TODO: Investigate this
26208 				return document.body;
26209 			}
26210 		}
26211 
26212 		// We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
26213 		// TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
26214 		function createBookmark(dom, rng) {
26215 			if (rng && rng.startContainer) {
26216 				// Verify that the range is within the root of the editor
26217 				if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) {
26218 					return;
26219 				}
26220 
26221 				return {
26222 					startContainer: rng.startContainer,
26223 					startOffset: rng.startOffset,
26224 					endContainer: rng.endContainer,
26225 					endOffset: rng.endOffset
26226 				};
26227 			}
26228 
26229 			return rng;
26230 		}
26231 
26232 		function bookmarkToRng(editor, bookmark) {
26233 			var rng;
26234 
26235 			if (bookmark.startContainer) {
26236 				rng = editor.getDoc().createRange();
26237 				rng.setStart(bookmark.startContainer, bookmark.startOffset);
26238 				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
26239 			} else {
26240 				rng = bookmark;
26241 			}
26242 
26243 			return rng;
26244 		}
26245 
26246 		function isUIElement(elm) {
26247 			return !!DOM.getParent(elm, FocusManager.isEditorUIElement);
26248 		}
26249 
26250 		function registerEvents(e) {
26251 			var editor = e.editor;
26252 
26253 			editor.on('init', function() {
26254 				// Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab
26255 				if (editor.inline || Env.ie) {
26256 					// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
26257 					editor.on('nodechange keyup', function() {
26258 						var node = document.activeElement;
26259 
26260 						// IE 11 reports active element as iframe not body of iframe
26261 						if (node && node.id == editor.id + '_ifr') {
26262 							node = editor.getBody();
26263 						}
26264 
26265 						if (editor.dom.isChildOf(node, editor.getBody())) {
26266 							editor.lastRng = editor.selection.getRng();
26267 						}
26268 					});
26269 
26270 					// Handles the issue with WebKit not retaining selection within inline document
26271 					// If the user releases the mouse out side the body since a mouse up event wont occur on the body
26272 					if (Env.webkit && !selectionChangeHandler) {
26273 						selectionChangeHandler = function() {
26274 							var activeEditor = editorManager.activeEditor;
26275 
26276 							if (activeEditor && activeEditor.selection) {
26277 								var rng = activeEditor.selection.getRng();
26278 
26279 								// Store when it's non collapsed
26280 								if (rng && !rng.collapsed) {
26281 									editor.lastRng = rng;
26282 								}
26283 							}
26284 						};
26285 
26286 						DOM.bind(document, 'selectionchange', selectionChangeHandler);
26287 					}
26288 				}
26289 			});
26290 
26291 			editor.on('setcontent', function() {
26292 				editor.lastRng = null;
26293 			});
26294 
26295 			// Remove last selection bookmark on mousedown see #6305
26296 			editor.on('mousedown', function() {
26297 				editor.selection.lastFocusBookmark = null;
26298 			});
26299 
26300 			editor.on('focusin', function() {
26301 				var focusedEditor = editorManager.focusedEditor;
26302 
26303 				if (editor.selection.lastFocusBookmark) {
26304 					editor.selection.setRng(bookmarkToRng(editor, editor.selection.lastFocusBookmark));
26305 					editor.selection.lastFocusBookmark = null;
26306 				}
26307 
26308 				if (focusedEditor != editor) {
26309 					if (focusedEditor) {
26310 						focusedEditor.fire('blur', {focusedEditor: editor});
26311 					}
26312 
26313 					editorManager.activeEditor = editor;
26314 					editorManager.focusedEditor = editor;
26315 					editor.fire('focus', {blurredEditor: focusedEditor});
26316 					editor.focus(true);
26317 				}
26318 
26319 				editor.lastRng = null;
26320 			});
26321 
26322 			editor.on('focusout', function() {
26323 				window.setTimeout(function() {
26324 					var focusedEditor = editorManager.focusedEditor;
26325 
26326 					// Still the same editor the the blur was outside any editor UI
26327 					if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
26328 						editor.fire('blur', {focusedEditor: null});
26329 						editorManager.focusedEditor = null;
26330 
26331 						// Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs
26332 						if (editor.selection) {
26333 							editor.selection.lastFocusBookmark = null;
26334 						}
26335 					}
26336 				}, 0);
26337 			});
26338 
26339 			// Check if focus is moved to an element outside the active editor by checking if the target node
26340 			// isn't within the body of the activeEditor nor a UI element such as a dialog child control
26341 			if (!documentFocusInHandler) {
26342 				documentFocusInHandler = function(e) {
26343 					var activeEditor = editorManager.activeEditor;
26344 
26345 					if (activeEditor && e.target.ownerDocument == document) {
26346 						// Check to make sure we have a valid selection
26347 						if (activeEditor.selection) {
26348 							activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng);
26349 						}
26350 
26351 						// Fire a blur event if the element isn't a UI element
26352 						if (!isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
26353 							activeEditor.fire('blur', {focusedEditor: null});
26354 							editorManager.focusedEditor = null;
26355 						}
26356 					}
26357 				};
26358 
26359 				DOM.bind(document, 'focusin', documentFocusInHandler);
26360 			}
26361 
26362 			// Handle edge case when user starts the selection inside the editor and releases
26363 			// the mouse outside the editor producing a new selection. This weird workaround is needed since
26364 			// Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843
26365 			if (editor.inline && !documentMouseUpHandler) {
26366 				documentMouseUpHandler = function(e) {
26367 					var activeEditor = editorManager.activeEditor;
26368 
26369 					if (activeEditor.inline && !activeEditor.dom.isChildOf(e.target, activeEditor.getBody())) {
26370 						var rng = activeEditor.selection.getRng();
26371 
26372 						if (!rng.collapsed) {
26373 							activeEditor.lastRng = rng;
26374 						}
26375 					}
26376 				};
26377 
26378 				DOM.bind(document, 'mouseup', documentMouseUpHandler);
26379 			}
26380 		}
26381 
26382 		function unregisterDocumentEvents(e) {
26383 			if (editorManager.focusedEditor == e.editor) {
26384 				editorManager.focusedEditor = null;
26385 			}
26386 
26387 			if (!editorManager.activeEditor) {
26388 				DOM.unbind(document, 'selectionchange', selectionChangeHandler);
26389 				DOM.unbind(document, 'focusin', documentFocusInHandler);
26390 				DOM.unbind(document, 'mouseup', documentMouseUpHandler);
26391 				selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null;
26392 			}
26393 		}
26394 
26395 		editorManager.on('AddEditor', registerEvents);
26396 		editorManager.on('RemoveEditor', unregisterDocumentEvents);
26397 	}
26398 
26399 	/**
26400 	 * Returns true if the specified element is part of the UI for example an button or text input.
26401 	 *
26402 	 * @method isEditorUIElement
26403 	 * @param  {Element} elm Element to check if it's part of the UI or not.
26404 	 * @return {Boolean} True/false state if the element is part of the UI or not.
26405 	 */
26406 	FocusManager.isEditorUIElement = function(elm) {
26407 		// Needs to be converted to string since svg can have focus: #6776
26408 		return elm.className.toString().indexOf('mce-') !== -1;
26409 	};
26410 
26411 	return FocusManager;
26412 });
26413 
26414 // Included from: js/tinymce/classes/EditorManager.js
26415 
26416 /**
26417  * EditorManager.js
26418  *
26419  * Copyright, Moxiecode Systems AB
26420  * Released under LGPL License.
26421  *
26422  * License: http://www.tinymce.com/license
26423  * Contributing: http://www.tinymce.com/contributing
26424  */
26425 
26426 /**
26427  * This class used as a factory for manager for tinymce.Editor instances.
26428  *
26429  * @example
26430  * tinymce.EditorManager.init({});
26431  *
26432  * @class tinymce.EditorManager
26433  * @mixes tinymce.util.Observable
26434  * @static
26435  */
26436 define("tinymce/EditorManager", [
26437 	"tinymce/Editor",
26438 	"tinymce/dom/DOMUtils",
26439 	"tinymce/util/URI",
26440 	"tinymce/Env",
26441 	"tinymce/util/Tools",
26442 	"tinymce/util/Observable",
26443 	"tinymce/util/I18n",
26444 	"tinymce/FocusManager"
26445 ], function(Editor, DOMUtils, URI, Env, Tools, Observable, I18n, FocusManager) {
26446 	var DOM = DOMUtils.DOM;
26447 	var explode = Tools.explode, each = Tools.each, extend = Tools.extend;
26448 	var instanceCounter = 0, beforeUnloadDelegate, EditorManager;
26449 
26450 	function removeEditorFromList(editor) {
26451 		var editors = EditorManager.editors, removedFromList;
26452 
26453 		delete editors[editor.id];
26454 
26455 		for (var i = 0; i < editors.length; i++) {
26456 			if (editors[i] == editor) {
26457 				editors.splice(i, 1);
26458 				removedFromList = true;
26459 				break;
26460 			}
26461 		}
26462 
26463 		// Select another editor since the active one was removed
26464 		if (EditorManager.activeEditor == editor) {
26465 			EditorManager.activeEditor = editors[0];
26466 		}
26467 
26468 		// Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor
26469 		if (EditorManager.focusedEditor == editor) {
26470 			EditorManager.focusedEditor = null;
26471 		}
26472 
26473 		return removedFromList;
26474 	}
26475 
26476 	function purgeDestroyedEditor(editor) {
26477 		// User has manually destroyed the editor lets clean up the mess
26478 		if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
26479 			removeEditorFromList(editor);
26480 			editor.destroy(true);
26481 			editor = null;
26482 		}
26483 
26484 		return editor;
26485 	}
26486 
26487 	EditorManager = {
26488 		/**
26489 		 * Major version of TinyMCE build.
26490 		 *
26491 		 * @property majorVersion
26492 		 * @type String
26493 		 */
26494 		majorVersion : '4',
26495 
26496 		/**
26497 		 * Minor version of TinyMCE build.
26498 		 *
26499 		 * @property minorVersion
26500 		 * @type String
26501 		 */
26502 		minorVersion : '0.25',
26503 
26504 		/**
26505 		 * Release date of TinyMCE build.
26506 		 *
26507 		 * @property releaseDate
26508 		 * @type String
26509 		 */
26510 		releaseDate: '2014-04-30',
26511 
26512 		/**
26513 		 * Collection of editor instances.
26514 		 *
26515 		 * @property editors
26516 		 * @type Object
26517 		 * @example
26518 		 * for (edId in tinymce.editors)
26519 		 *     tinymce.editors[edId].save();
26520 		 */
26521 		editors: [],
26522 
26523 		/**
26524 		 * Collection of language pack data.
26525 		 *
26526 		 * @property i18n
26527 		 * @type Object
26528 		 */
26529 		i18n: I18n,
26530 
26531 		/**
26532 		 * Currently active editor instance.
26533 		 *
26534 		 * @property activeEditor
26535 		 * @type tinymce.Editor
26536 		 * @example
26537 		 * tinyMCE.activeEditor.selection.getContent();
26538 		 * tinymce.EditorManager.activeEditor.selection.getContent();
26539 		 */
26540 		activeEditor: null,
26541 
26542 		setup: function() {
26543 			var self = this, baseURL, documentBaseURL, suffix = "", preInit, src;
26544 
26545 			// Get base URL for the current document
26546 			documentBaseURL = document.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
26547 			if (!/[\/\\]$/.test(documentBaseURL)) {
26548 				documentBaseURL += '/';
26549 			}
26550 
26551 			// If tinymce is defined and has a base use that or use the old tinyMCEPreInit
26552 			preInit = window.tinymce || window.tinyMCEPreInit;
26553 			if (preInit) {
26554 				baseURL = preInit.base || preInit.baseURL;
26555 				suffix = preInit.suffix;
26556 			} else {
26557 				// Get base where the tinymce script is located
26558 				var scripts = document.getElementsByTagName('script');
26559 				for (var i = 0; i < scripts.length; i++) {
26560 					src = scripts[i].src;
26561 
26562 					// Script types supported:
26563 					// tinymce.js tinymce.min.js tinymce.dev.js
26564 					// tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js
26565 					// tinymce.full.js tinymce.full.min.js tinymce.full.dev.js
26566 					if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
26567 						if (src.indexOf('.min') != -1) {
26568 							suffix = '.min';
26569 						}
26570 
26571 						baseURL = src.substring(0, src.lastIndexOf('/'));
26572 						break;
26573 					}
26574 				}
26575 
26576 				// We didn't find any baseURL by looking at the script elements
26577 				// Try to use the document.currentScript as a fallback
26578 				if (!baseURL && document.currentScript) {
26579 					src = document.currentScript.src;
26580 
26581 					if (src.indexOf('.min') != -1) {
26582 						suffix = '.min';
26583 					}
26584 
26585 					baseURL = src.substring(0, src.lastIndexOf('/'));
26586 				}
26587 			}
26588 
26589 			/**
26590 			 * Base URL where the root directory if TinyMCE is located.
26591 			 *
26592 			 * @property baseURL
26593 			 * @type String
26594 			 */
26595 			self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);
26596 
26597 			/**
26598 			 * Document base URL where the current document is located.
26599 			 *
26600 			 * @property documentBaseURL
26601 			 * @type String
26602 			 */
26603 			self.documentBaseURL = documentBaseURL;
26604 
26605 			/**
26606 			 * Absolute baseURI for the installation path of TinyMCE.
26607 			 *
26608 			 * @property baseURI
26609 			 * @type tinymce.util.URI
26610 			 */
26611 			self.baseURI = new URI(self.baseURL);
26612 
26613 			/**
26614 			 * Current suffix to add to each plugin/theme that gets loaded for example ".min".
26615 			 *
26616 			 * @property suffix
26617 			 * @type String
26618 			 */
26619 			self.suffix = suffix;
26620 
26621 			self.focusManager = new FocusManager(self);
26622 		},
26623 
26624 		/**
26625 		 * Initializes a set of editors. This method will create editors based on various settings.
26626 		 *
26627 		 * @method init
26628 		 * @param {Object} settings Settings object to be passed to each editor instance.
26629 		 * @example
26630 		 * // Initializes a editor using the longer method
26631 		 * tinymce.EditorManager.init({
26632 		 *    some_settings : 'some value'
26633 		 * });
26634 		 *
26635 		 * // Initializes a editor instance using the shorter version
26636 		 * tinyMCE.init({
26637 		 *    some_settings : 'some value'
26638 		 * });
26639 		 */
26640 		init: function(settings) {
26641 			var self = this, editors = [], editor;
26642 
26643 			function createId(elm) {
26644 				var id = elm.id;
26645 
26646 				// Use element id, or unique name or generate a unique id
26647 				if (!id) {
26648 					id = elm.name;
26649 
26650 					if (id && !DOM.get(id)) {
26651 						id = elm.name;
26652 					} else {
26653 						// Generate unique name
26654 						id = DOM.uniqueId();
26655 					}
26656 
26657 					elm.setAttribute('id', id);
26658 				}
26659 
26660 				return id;
26661 			}
26662 
26663 			function createEditor(id, settings) {
26664 				if (!purgeDestroyedEditor(self.get(id))) {
26665 					var editor = new Editor(id, settings, self);
26666 					editors.push(editor);
26667 					editor.render();
26668 				}
26669 			}
26670 
26671 			function execCallback(se, n, s) {
26672 				var f = se[n];
26673 
26674 				if (!f) {
26675 					return;
26676 				}
26677 
26678 				return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
26679 			}
26680 
26681 			function hasClass(n, c) {
26682 				return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
26683 			}
26684 
26685 			function readyHandler() {
26686 				var l, co;
26687 
26688 				DOM.unbind(window, 'ready', readyHandler);
26689 
26690 				execCallback(settings, 'onpageload');
26691 
26692 				if (settings.types) {
26693 					// Process type specific selector
26694 					each(settings.types, function(type) {
26695 						each(DOM.select(type.selector), function(elm) {
26696 							createEditor(createId(elm), extend({}, settings, type));
26697 						});
26698 					});
26699 
26700 					return;
26701 				} else if (settings.selector) {
26702 					// Process global selector
26703 					each(DOM.select(settings.selector), function(elm) {
26704 						createEditor(createId(elm), settings);
26705 					});
26706 
26707 					return;
26708 				}
26709 
26710 				// Fallback to old setting
26711 				switch (settings.mode) {
26712 					case "exact":
26713 						l = settings.elements || '';
26714 
26715 						if (l.length > 0) {
26716 							each(explode(l), function(v) {
26717 								if (DOM.get(v)) {
26718 									editor = new Editor(v, settings, self);
26719 									editors.push(editor);
26720 									editor.render();
26721 								} else {
26722 									each(document.forms, function(f) {
26723 										each(f.elements, function(e) {
26724 											if (e.name === v) {
26725 												v = 'mce_editor_' + instanceCounter++;
26726 												DOM.setAttrib(e, 'id', v);
26727 												createEditor(v, settings);
26728 											}
26729 										});
26730 									});
26731 								}
26732 							});
26733 						}
26734 						break;
26735 
26736 					case "textareas":
26737 					case "specific_textareas":
26738 						each(DOM.select('textarea'), function(elm) {
26739 							if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) {
26740 								return;
26741 							}
26742 
26743 							if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) {
26744 								createEditor(createId(elm), settings);
26745 							}
26746 						});
26747 						break;
26748 				}
26749 
26750 				// Call onInit when all editors are initialized
26751 				if (settings.oninit) {
26752 					l = co = 0;
26753 
26754 					each(editors, function(ed) {
26755 						co++;
26756 
26757 						if (!ed.initialized) {
26758 							// Wait for it
26759 							ed.on('init', function() {
26760 								l++;
26761 
26762 								// All done
26763 								if (l == co) {
26764 									execCallback(settings, 'oninit');
26765 								}
26766 							});
26767 						} else {
26768 							l++;
26769 						}
26770 
26771 						// All done
26772 						if (l == co) {
26773 							execCallback(settings, 'oninit');
26774 						}
26775 					});
26776 				}
26777 			}
26778 
26779 			self.settings = settings;
26780 
26781 			DOM.bind(window, 'ready', readyHandler);
26782 		},
26783 
26784 		/**
26785 		 * Returns a editor instance by id.
26786 		 *
26787 		 * @method get
26788 		 * @param {String/Number} id Editor instance id or index to return.
26789 		 * @return {tinymce.Editor} Editor instance to return.
26790 		 * @example
26791 		 * // Adds an onclick event to an editor by id (shorter version)
26792 		 * tinymce.get('mytextbox').on('click', function(e) {
26793 		 *    ed.windowManager.alert('Hello world!');
26794 		 * });
26795 		 *
26796 		 * // Adds an onclick event to an editor by id (longer version)
26797 		 * tinymce.EditorManager.get('mytextbox').on('click', function(e) {
26798 		 *    ed.windowManager.alert('Hello world!');
26799 		 * });
26800 		 */
26801 		get: function(id) {
26802 			if (!arguments.length) {
26803 				return this.editors;
26804 			}
26805 
26806 			return id in this.editors ? this.editors[id] : null;
26807 		},
26808 
26809 		/**
26810 		 * Adds an editor instance to the editor collection. This will also set it as the active editor.
26811 		 *
26812 		 * @method add
26813 		 * @param {tinymce.Editor} editor Editor instance to add to the collection.
26814 		 * @return {tinymce.Editor} The same instance that got passed in.
26815 		 */
26816 		add: function(editor) {
26817 			var self = this, editors = self.editors;
26818 
26819 			// Add named and index editor instance
26820 			editors[editor.id] = editor;
26821 			editors.push(editor);
26822 
26823 			self.activeEditor = editor;
26824 
26825 			/**
26826 			 * Fires when an editor is added to the EditorManager collection.
26827 			 *
26828 			 * @event AddEditor
26829 			 * @param {Object} e Event arguments.
26830 			 */
26831 			self.fire('AddEditor', {editor: editor});
26832 
26833 			if (!beforeUnloadDelegate) {
26834 				beforeUnloadDelegate = function() {
26835 					self.fire('BeforeUnload');
26836 				};
26837 
26838 				DOM.bind(window, 'beforeunload', beforeUnloadDelegate);
26839 			}
26840 
26841 			return editor;
26842 		},
26843 
26844 		/**
26845 		 * Creates an editor instance and adds it to the EditorManager collection.
26846 		 *
26847 		 * @method createEditor
26848 		 * @param {String} id Instance id to use for editor.
26849 		 * @param {Object} settings Editor instance settings.
26850 		 * @return {tinymce.Editor} Editor instance that got created.
26851 		 */
26852 		createEditor: function(id, settings) {
26853 			return this.add(new Editor(id, settings, this));
26854 		},
26855 
26856 		/**
26857 		 * Removes a editor or editors form page.
26858 		 *
26859 		 * @example
26860 		 * // Remove all editors bound to divs
26861 		 * tinymce.remove('div');
26862 		 *
26863 		 * // Remove all editors bound to textareas
26864 		 * tinymce.remove('textarea');
26865 		 *
26866 		 * // Remove all editors
26867 		 * tinymce.remove();
26868 		 *
26869 		 * // Remove specific instance by id
26870 		 * tinymce.remove('#id');
26871 		 *
26872 		 * @method remove
26873 		 * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove.
26874 		 * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
26875 		 */
26876 		remove: function(selector) {
26877 			var self = this, i, editors = self.editors, editor;
26878 
26879 			// Remove all editors
26880 			if (!selector) {
26881 				for (i = editors.length - 1; i >= 0; i--) {
26882 					self.remove(editors[i]);
26883 				}
26884 
26885 				return;
26886 			}
26887 
26888 			// Remove editors by selector
26889 			if (typeof(selector) == "string") {
26890 				selector = selector.selector || selector;
26891 
26892 				each(DOM.select(selector), function(elm) {
26893 					self.remove(editors[elm.id]);
26894 				});
26895 
26896 				return;
26897 			}
26898 
26899 			// Remove specific editor
26900 			editor = selector;
26901 
26902 			// Not in the collection
26903 			if (!editors[editor.id]) {
26904 				return null;
26905 			}
26906 
26907 			/**
26908 			 * Fires when an editor is removed from EditorManager collection.
26909 			 *
26910 			 * @event RemoveEditor
26911 			 * @param {Object} e Event arguments.
26912 			 */
26913 			if (removeEditorFromList(editor)) {
26914 				self.fire('RemoveEditor', {editor: editor});
26915 			}
26916 
26917 			if (!editors.length) {
26918 				DOM.unbind(window, 'beforeunload', beforeUnloadDelegate);
26919 			}
26920 
26921 			editor.remove();
26922 
26923 			return editor;
26924 		},
26925 
26926 		/**
26927 		 * Executes a specific command on the currently active editor.
26928 		 *
26929 		 * @method execCommand
26930 		 * @param {String} c Command to perform for example Bold.
26931 		 * @param {Boolean} u Optional boolean state if a UI should be presented for the command or not.
26932 		 * @param {String} v Optional value parameter like for example an URL to a link.
26933 		 * @return {Boolean} true/false if the command was executed or not.
26934 		 */
26935 		execCommand: function(cmd, ui, value) {
26936 			var self = this, editor = self.get(value);
26937 
26938 			// Manager commands
26939 			switch (cmd) {
26940 				case "mceAddEditor":
26941 					if (!self.get(value)) {
26942 						new Editor(value, self.settings, self).render();
26943 					}
26944 
26945 					return true;
26946 
26947 				case "mceRemoveEditor":
26948 					if (editor) {
26949 						editor.remove();
26950 					}
26951 
26952 					return true;
26953 
26954 				case 'mceToggleEditor':
26955 					if (!editor) {
26956 						self.execCommand('mceAddEditor', 0, value);
26957 						return true;
26958 					}
26959 
26960 					if (editor.isHidden()) {
26961 						editor.show();
26962 					} else {
26963 						editor.hide();
26964 					}
26965 
26966 					return true;
26967 			}
26968 
26969 			// Run command on active editor
26970 			if (self.activeEditor) {
26971 				return self.activeEditor.execCommand(cmd, ui, value);
26972 			}
26973 
26974 			return false;
26975 		},
26976 
26977 		/**
26978 		 * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
26979 		 *
26980 		 * @method triggerSave
26981 		 * @example
26982 		 * // Saves all contents
26983 		 * tinyMCE.triggerSave();
26984 		 */
26985 		triggerSave: function() {
26986 			each(this.editors, function(editor) {
26987 				editor.save();
26988 			});
26989 		},
26990 
26991 		/**
26992 		 * Adds a language pack, this gets called by the loaded language files like en.js.
26993 		 *
26994 		 * @method addI18n
26995 		 * @param {String} code Optional language code.
26996 		 * @param {Object} items Name/value object with translations.
26997 		 */
26998 		addI18n: function(code, items) {
26999 			I18n.add(code, items);
27000 		},
27001 
27002 		/**
27003 		 * Translates the specified string using the language pack items.
27004 		 *
27005 		 * @method translate
27006 		 * @param {String/Array/Object} text String to translate
27007 		 * @return {String} Translated string.
27008 		 */
27009 		translate: function(text) {
27010 			return I18n.translate(text);
27011 		}
27012 	};
27013 
27014 	extend(EditorManager, Observable);
27015 
27016 	EditorManager.setup();
27017 
27018 	// Export EditorManager as tinymce/tinymce in global namespace
27019 	window.tinymce = window.tinyMCE = EditorManager;
27020 
27021 	return EditorManager;
27022 });
27023 
27024 // Included from: js/tinymce/classes/LegacyInput.js
27025 
27026 /**
27027  * LegacyInput.js
27028  *
27029  * Copyright, Moxiecode Systems AB
27030  * Released under LGPL License.
27031  *
27032  * License: http://www.tinymce.com/license
27033  * Contributing: http://www.tinymce.com/contributing
27034  */
27035 
27036 define("tinymce/LegacyInput", [
27037 	"tinymce/EditorManager",
27038 	"tinymce/util/Tools"
27039 ], function(EditorManager, Tools) {
27040 	var each = Tools.each, explode = Tools.explode;
27041 
27042 	EditorManager.on('AddEditor', function(e) {
27043 		var editor = e.editor;
27044 
27045 		editor.on('preInit', function() {
27046 			var filters, fontSizes, dom, settings = editor.settings;
27047 
27048 			function replaceWithSpan(node, styles) {
27049 				each(styles, function(value, name) {
27050 					if (value) {
27051 						dom.setStyle(node, name, value);
27052 					}
27053 				});
27054 
27055 				dom.rename(node, 'span');
27056 			}
27057 
27058 			function convert(e) {
27059 				dom = editor.dom;
27060 
27061 				if (settings.convert_fonts_to_spans) {
27062 					each(dom.select('font,u,strike', e.node), function(node) {
27063 						filters[node.nodeName.toLowerCase()](dom, node);
27064 					});
27065 				}
27066 			}
27067 
27068 			if (settings.inline_styles) {
27069 				fontSizes = explode(settings.font_size_legacy_values);
27070 
27071 				filters = {
27072 					font: function(dom, node) {
27073 						replaceWithSpan(node, {
27074 							backgroundColor: node.style.backgroundColor,
27075 							color: node.color,
27076 							fontFamily: node.face,
27077 							fontSize: fontSizes[parseInt(node.size, 10) - 1]
27078 						});
27079 					},
27080 
27081 					u: function(dom, node) {
27082 						replaceWithSpan(node, {
27083 							textDecoration: 'underline'
27084 						});
27085 					},
27086 
27087 					strike: function(dom, node) {
27088 						replaceWithSpan(node, {
27089 							textDecoration: 'line-through'
27090 						});
27091 					}
27092 				};
27093 
27094 				editor.on('PreProcess SetContent', convert);
27095 			}
27096 		});
27097 	});
27098 });
27099 
27100 // Included from: js/tinymce/classes/util/XHR.js
27101 
27102 /**
27103  * XHR.js
27104  *
27105  * Copyright, Moxiecode Systems AB
27106  * Released under LGPL License.
27107  *
27108  * License: http://www.tinymce.com/license
27109  * Contributing: http://www.tinymce.com/contributing
27110  */
27111 
27112 /**
27113  * This class enables you to send XMLHTTPRequests cross browser.
27114  * @class tinymce.util.XHR
27115  * @static
27116  * @example
27117  * // Sends a low level Ajax request
27118  * tinymce.util.XHR.send({
27119  *    url: 'someurl',
27120  *    success: function(text) {
27121  *       console.debug(text);
27122  *    }
27123  * });
27124  */
27125 define("tinymce/util/XHR", [], function() {
27126 	return {
27127 		/**
27128 		 * Sends a XMLHTTPRequest.
27129 		 * Consult the Wiki for details on what settings this method takes.
27130 		 *
27131 		 * @method send
27132 		 * @param {Object} settings Object will target URL, callbacks and other info needed to make the request.
27133 		 */
27134 		send: function(settings) {
27135 			var xhr, count = 0;
27136 
27137 			function ready() {
27138 				if (!settings.async || xhr.readyState == 4 || count++ > 10000) {
27139 					if (settings.success && count < 10000 && xhr.status == 200) {
27140 						settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings);
27141 					} else if (settings.error) {
27142 						settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings);
27143 					}
27144 
27145 					xhr = null;
27146 				} else {
27147 					setTimeout(ready, 10);
27148 				}
27149 			}
27150 
27151 			// Default settings
27152 			settings.scope = settings.scope || this;
27153 			settings.success_scope = settings.success_scope || settings.scope;
27154 			settings.error_scope = settings.error_scope || settings.scope;
27155 			settings.async = settings.async === false ? false : true;
27156 			settings.data = settings.data || '';
27157 
27158 			xhr = new XMLHttpRequest();
27159 
27160 			if (xhr) {
27161 				if (xhr.overrideMimeType) {
27162 					xhr.overrideMimeType(settings.content_type);
27163 				}
27164 
27165 				xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
27166 
27167 				if (settings.content_type) {
27168 					xhr.setRequestHeader('Content-Type', settings.content_type);
27169 				}
27170 
27171 				xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
27172 
27173 				xhr.send(settings.data);
27174 
27175 				// Syncronous request
27176 				if (!settings.async) {
27177 					return ready();
27178 				}
27179 
27180 				// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
27181 				setTimeout(ready, 10);
27182 			}
27183 		}
27184 	};
27185 });
27186 
27187 // Included from: js/tinymce/classes/util/JSON.js
27188 
27189 /**
27190  * JSON.js
27191  *
27192  * Copyright, Moxiecode Systems AB
27193  * Released under LGPL License.
27194  *
27195  * License: http://www.tinymce.com/license
27196  * Contributing: http://www.tinymce.com/contributing
27197  */
27198 
27199 /**
27200  * JSON parser and serializer class.
27201  *
27202  * @class tinymce.util.JSON
27203  * @static
27204  * @example
27205  * // JSON parse a string into an object
27206  * var obj = tinymce.util.JSON.parse(somestring);
27207  *
27208  * // JSON serialize a object into an string
27209  * var str = tinymce.util.JSON.serialize(obj);
27210  */
27211 define("tinymce/util/JSON", [], function() {
27212 	function serialize(o, quote) {
27213 		var i, v, t, name;
27214 
27215 		quote = quote || '"';
27216 
27217 		if (o === null) {
27218 			return 'null';
27219 		}
27220 
27221 		t = typeof o;
27222 
27223 		if (t == 'string') {
27224 			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
27225 
27226 			return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
27227 				// Make sure single quotes never get encoded inside double quotes for JSON compatibility
27228 				if (quote === '"' && a === "'") {
27229 					return a;
27230 				}
27231 
27232 				i = v.indexOf(b);
27233 
27234 				if (i + 1) {
27235 					return '\\' + v.charAt(i + 1);
27236 				}
27237 
27238 				a = b.charCodeAt().toString(16);
27239 
27240 				return '\\u' + '0000'.substring(a.length) + a;
27241 			}) + quote;
27242 		}
27243 
27244 		if (t == 'object') {
27245 			if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
27246 					for (i = 0, v = '['; i < o.length; i++) {
27247 						v += (i > 0 ? ',' : '') + serialize(o[i], quote);
27248 					}
27249 
27250 					return v + ']';
27251 				}
27252 
27253 				v = '{';
27254 
27255 				for (name in o) {
27256 					if (o.hasOwnProperty(name)) {
27257 						v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name +
27258 							quote + ':' + serialize(o[name], quote) : '';
27259 					}
27260 				}
27261 
27262 				return v + '}';
27263 		}
27264 
27265 		return '' + o;
27266 	}
27267 
27268 	return {
27269 		/**
27270 		 * Serializes the specified object as a JSON string.
27271 		 *
27272 		 * @method serialize
27273 		 * @param {Object} obj Object to serialize as a JSON string.
27274 		 * @param {String} quote Optional quote string defaults to ".
27275 		 * @return {string} JSON string serialized from input.
27276 		 */
27277 		serialize: serialize,
27278 
27279 		/**
27280 		 * Unserializes/parses the specified JSON string into a object.
27281 		 *
27282 		 * @method parse
27283 		 * @param {string} s JSON String to parse into a JavaScript object.
27284 		 * @return {Object} Object from input JSON string or undefined if it failed.
27285 		 */
27286 		parse: function(text) {
27287 			try {
27288 				// Trick uglify JS
27289 				return window[String.fromCharCode(101) + 'val']('(' + text + ')');
27290 			} catch (ex) {
27291 				// Ignore
27292 			}
27293 		}
27294 
27295 		/**#@-*/
27296 	};
27297 });
27298 
27299 // Included from: js/tinymce/classes/util/JSONRequest.js
27300 
27301 /**
27302  * JSONRequest.js
27303  *
27304  * Copyright, Moxiecode Systems AB
27305  * Released under LGPL License.
27306  *
27307  * License: http://www.tinymce.com/license
27308  * Contributing: http://www.tinymce.com/contributing
27309  */
27310 
27311 /**
27312  * This class enables you to use JSON-RPC to call backend methods.
27313  *
27314  * @class tinymce.util.JSONRequest
27315  * @example
27316  * var json = new tinymce.util.JSONRequest({
27317  *     url: 'somebackend.php'
27318  * });
27319  *
27320  * // Send RPC call 1
27321  * json.send({
27322  *     method: 'someMethod1',
27323  *     params: ['a', 'b'],
27324  *     success: function(result) {
27325  *         console.dir(result);
27326  *     }
27327  * });
27328  *
27329  * // Send RPC call 2
27330  * json.send({
27331  *     method: 'someMethod2',
27332  *     params: ['a', 'b'],
27333  *     success: function(result) {
27334  *         console.dir(result);
27335  *     }
27336  * });
27337  */
27338 define("tinymce/util/JSONRequest", [
27339 	"tinymce/util/JSON",
27340 	"tinymce/util/XHR",
27341 	"tinymce/util/Tools"
27342 ], function(JSON, XHR, Tools) {
27343 	var extend = Tools.extend;
27344 
27345 	function JSONRequest(settings) {
27346 		this.settings = extend({}, settings);
27347 		this.count = 0;
27348 	}
27349 
27350 	/**
27351 	 * Simple helper function to send a JSON-RPC request without the need to initialize an object.
27352 	 * Consult the Wiki API documentation for more details on what you can pass to this function.
27353 	 *
27354 	 * @method sendRPC
27355 	 * @static
27356 	 * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc.
27357 	 */
27358 	JSONRequest.sendRPC = function(o) {
27359 		return new JSONRequest().send(o);
27360 	};
27361 
27362 	JSONRequest.prototype = {
27363 		/**
27364 		 * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function.
27365 		 *
27366 		 * @method send
27367 		 * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc.
27368 		 */
27369 		send: function(args) {
27370 			var ecb = args.error, scb = args.success;
27371 
27372 			args = extend(this.settings, args);
27373 
27374 			args.success = function(c, x) {
27375 				c = JSON.parse(c);
27376 
27377 				if (typeof(c) == 'undefined') {
27378 					c = {
27379 						error : 'JSON Parse error.'
27380 					};
27381 				}
27382 
27383 				if (c.error) {
27384 					ecb.call(args.error_scope || args.scope, c.error, x);
27385 				} else {
27386 					scb.call(args.success_scope || args.scope, c.result);
27387 				}
27388 			};
27389 
27390 			args.error = function(ty, x) {
27391 				if (ecb) {
27392 					ecb.call(args.error_scope || args.scope, ty, x);
27393 				}
27394 			};
27395 
27396 			args.data = JSON.serialize({
27397 				id: args.id || 'c' + (this.count++),
27398 				method: args.method,
27399 				params: args.params
27400 			});
27401 
27402 			// JSON content type for Ruby on rails. Bug: #1883287
27403 			args.content_type = 'application/json';
27404 
27405 			XHR.send(args);
27406 		}
27407 	};
27408 
27409 	return JSONRequest;
27410 });
27411 
27412 // Included from: js/tinymce/classes/util/JSONP.js
27413 
27414 /**
27415  * JSONP.js
27416  *
27417  * Copyright, Moxiecode Systems AB
27418  * Released under LGPL License.
27419  *
27420  * License: http://www.tinymce.com/license
27421  * Contributing: http://www.tinymce.com/contributing
27422  */
27423 
27424 define("tinymce/util/JSONP", [
27425 	"tinymce/dom/DOMUtils"
27426 ], function(DOMUtils) {
27427 	return {
27428 		callbacks: {},
27429 		count: 0,
27430 
27431 		send: function(settings) {
27432 			var self = this, dom = DOMUtils.DOM, count = settings.count !== undefined ? settings.count : self.count;
27433 			var id = 'tinymce_jsonp_' + count;
27434 
27435 			self.callbacks[count] = function(json) {
27436 				dom.remove(id);
27437 				delete self.callbacks[count];
27438 
27439 				settings.callback(json);
27440 			};
27441 
27442 			dom.add(dom.doc.body, 'script', {
27443 				id: id,
27444 				src: settings.url,
27445 				type: 'text/javascript'
27446 			});
27447 
27448 			self.count++;
27449 		}
27450 	};
27451 });
27452 
27453 // Included from: js/tinymce/classes/util/LocalStorage.js
27454 
27455 /**
27456  * LocalStorage.js
27457  *
27458  * Copyright, Moxiecode Systems AB
27459  * Released under LGPL License.
27460  *
27461  * License: http://www.tinymce.com/license
27462  * Contributing: http://www.tinymce.com/contributing
27463  */
27464 
27465 /**
27466  * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers.
27467  * Storage is done using userData on IE 7 and a special serialization format. The format is designed
27468  * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This
27469  * makes it possible to store for example HTML data.
27470  *
27471  * Storage format for userData:
27472  * <base 32 key length>,<key string>,<base 32 value length>,<value>,...
27473  *
27474  * For example this data key1=value1,key2=value2 would be:
27475  * 4,key1,6,value1,4,key2,6,value2
27476  *
27477  * @class tinymce.util.LocalStorage
27478  * @static
27479  * @version 4.0
27480  * @example
27481  * tinymce.util.LocalStorage.setItem('key', 'value');
27482  * var value = tinymce.util.LocalStorage.getItem('key');
27483  */
27484 define("tinymce/util/LocalStorage", [], function() {
27485 	var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;
27486 
27487 	// Check for native support
27488 	try {
27489 		if (window.localStorage) {
27490 			return localStorage;
27491 		}
27492 	} catch (ex) {
27493 		// Ignore
27494 	}
27495 
27496 	userDataKey = "tinymce";
27497 	storageElm = document.documentElement;
27498 	hasOldIEDataSupport = !!storageElm.addBehavior;
27499 
27500 	if (hasOldIEDataSupport) {
27501 		storageElm.addBehavior('#default#userData');
27502 	}
27503 
27504 	/**
27505 	 * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
27506 	 */
27507 	function updateKeys() {
27508 		keys = [];
27509 
27510 		for (var key in items) {
27511 			keys.push(key);
27512 		}
27513 
27514 		LocalStorage.length = keys.length;
27515 	}
27516 
27517 	/**
27518 	 * Loads the userData string and parses it into the items structure.
27519 	 */
27520 	function load() {
27521 		var key, data, value, pos = 0;
27522 
27523 		items = {};
27524 
27525 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
27526 		if (!hasOldIEDataSupport) {
27527 			return;
27528 		}
27529 
27530 		function next(end) {
27531 			var value, nextPos;
27532 
27533 			nextPos = end !== undefined ? pos + end : data.indexOf(',', pos);
27534 			if (nextPos === -1 || nextPos > data.length) {
27535 				return null;
27536 			}
27537 
27538 			value = data.substring(pos, nextPos);
27539 			pos = nextPos + 1;
27540 
27541 			return value;
27542 		}
27543 
27544 		storageElm.load(userDataKey);
27545 		data = storageElm.getAttribute(userDataKey) || '';
27546 
27547 		do {
27548 			var offset = next();
27549 			if (offset === null) {
27550 				break;
27551 			}
27552 
27553 			key = next(parseInt(offset, 32) || 0);
27554 			if (key !== null) {
27555 				offset = next();
27556 				if (offset === null) {
27557 					break;
27558 				}
27559 
27560 				value = next(parseInt(offset, 32) || 0);
27561 
27562 				if (key) {
27563 					items[key] = value;
27564 				}
27565 			}
27566 		} while (key !== null);
27567 
27568 		updateKeys();
27569 	}
27570 
27571 	/**
27572 	 * Saves the items structure into a the userData format.
27573 	 */
27574 	function save() {
27575 		var value, data = '';
27576 
27577 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
27578 		if (!hasOldIEDataSupport) {
27579 			return;
27580 		}
27581 
27582 		for (var key in items) {
27583 			value = items[key];
27584 			data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
27585 		}
27586 
27587 		storageElm.setAttribute(userDataKey, data);
27588 
27589 		try {
27590 			storageElm.save(userDataKey);
27591 		} catch (ex) {
27592 			// Ignore disk full
27593 		}
27594 
27595 		updateKeys();
27596 	}
27597 
27598 	LocalStorage = {
27599 		/**
27600 		 * Length of the number of items in storage.
27601 		 *
27602 		 * @property length
27603 		 * @type Number
27604 		 * @return {Number} Number of items in storage.
27605 		 */
27606 		//length:0,
27607 
27608 		/**
27609 		 * Returns the key name by index.
27610 		 *
27611 		 * @method key
27612 		 * @param {Number} index Index of key to return.
27613 		 * @return {String} Key value or null if it wasn't found.
27614 		 */
27615 		key: function(index) {
27616 			return keys[index];
27617 		},
27618 
27619 		/**
27620 		 * Returns the value if the specified key or null if it wasn't found.
27621 		 *
27622 		 * @method getItem
27623 		 * @param {String} key Key of item to retrive.
27624 		 * @return {String} Value of the specified item or null if it wasn't found.
27625 		 */
27626 		getItem: function(key) {
27627 			return key in items ? items[key] : null;
27628 		},
27629 
27630 		/**
27631 		 * Sets the value of the specified item by it's key.
27632 		 *
27633 		 * @method setItem
27634 		 * @param {String} key Key of the item to set.
27635 		 * @param {String} value Value of the item to set.
27636 		 */
27637 		setItem: function(key, value) {
27638 			items[key] = "" + value;
27639 			save();
27640 		},
27641 
27642 		/**
27643 		 * Removes the specified item by key.
27644 		 *
27645 		 * @method removeItem
27646 		 * @param {String} key Key of item to remove.
27647 		 */
27648 		removeItem: function(key) {
27649 			delete items[key];
27650 			save();
27651 		},
27652 
27653 		/**
27654 		 * Removes all items.
27655 		 *
27656 		 * @method clear
27657 		 */
27658 		clear: function() {
27659 			items = {};
27660 			save();
27661 		}
27662 	};
27663 
27664 	load();
27665 
27666 	return LocalStorage;
27667 });
27668 
27669 // Included from: js/tinymce/classes/Compat.js
27670 
27671 /**
27672  * Compat.js
27673  *
27674  * Copyright, Moxiecode Systems AB
27675  * Released under LGPL License.
27676  *
27677  * License: http://www.tinymce.com/license
27678  * Contributing: http://www.tinymce.com/contributing
27679  */
27680 
27681 /**
27682  * TinyMCE core class.
27683  *
27684  * @static
27685  * @class tinymce
27686  * @borrow-members tinymce.EditorManager
27687  * @borrow-members tinymce.util.Tools
27688  */
27689 define("tinymce/Compat", [
27690 	"tinymce/dom/DOMUtils",
27691 	"tinymce/dom/EventUtils",
27692 	"tinymce/dom/ScriptLoader",
27693 	"tinymce/AddOnManager",
27694 	"tinymce/util/Tools",
27695 	"tinymce/Env"
27696 ], function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) {
27697 	var tinymce = window.tinymce;
27698 
27699 	/**
27700 	 * @property {tinymce.dom.DOMUtils} DOM Global DOM instance.
27701 	 * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance.
27702 	 * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance.
27703 	 * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance.
27704 	 */
27705 	tinymce.DOM = DOMUtils.DOM;
27706 	tinymce.ScriptLoader = ScriptLoader.ScriptLoader;
27707 	tinymce.PluginManager = AddOnManager.PluginManager;
27708 	tinymce.ThemeManager = AddOnManager.ThemeManager;
27709 
27710 	tinymce.dom = tinymce.dom || {};
27711 	tinymce.dom.Event = EventUtils.Event;
27712 
27713 	Tools.each(Tools, function(func, key) {
27714 		tinymce[key] = func;
27715 	});
27716 
27717 	Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) {
27718 		tinymce[name] = Env[name.substr(2).toLowerCase()];
27719 	});
27720 
27721 	return {};
27722 });
27723 
27724 // Describe the different namespaces
27725 
27726 /**
27727  * Root level namespace this contains classes directly releated to the TinyMCE editor.
27728  *
27729  * @namespace tinymce
27730  */
27731 
27732 /**
27733  * Contains classes for handling the browsers DOM.
27734  *
27735  * @namespace tinymce.dom
27736  */
27737 
27738 /**
27739  * Contains html parser and serializer logic.
27740  *
27741  * @namespace tinymce.html
27742  */
27743 
27744 /**
27745  * Contains the different UI types such as buttons, listboxes etc.
27746  *
27747  * @namespace tinymce.ui
27748  */
27749 
27750 /**
27751  * Contains various utility classes such as json parser, cookies etc.
27752  *
27753  * @namespace tinymce.util
27754  */
27755 
27756 // Included from: js/tinymce/classes/ui/Layout.js
27757 
27758 /**
27759  * Layout.js
27760  *
27761  * Copyright, Moxiecode Systems AB
27762  * Released under LGPL License.
27763  *
27764  * License: http://www.tinymce.com/license
27765  * Contributing: http://www.tinymce.com/contributing
27766  */
27767 
27768 /**
27769  * Base layout manager class.
27770  *
27771  * @class tinymce.ui.Layout
27772  */
27773 define("tinymce/ui/Layout", [
27774 	"tinymce/util/Class",
27775 	"tinymce/util/Tools"
27776 ], function(Class, Tools) {
27777 	"use strict";
27778 
27779 	return Class.extend({
27780 		Defaults: {
27781 			firstControlClass: 'first',
27782 			lastControlClass: 'last'
27783 		},
27784 
27785 		/**
27786 		 * Constructs a layout instance with the specified settings.
27787 		 *
27788 		 * @constructor
27789 		 * @param {Object} settings Name/value object with settings.
27790 		 */
27791 		init: function(settings) {
27792 			this.settings = Tools.extend({}, this.Defaults, settings);
27793 		},
27794 
27795 		/**
27796 		 * This method gets invoked before the layout renders the controls.
27797 		 *
27798 		 * @method preRender
27799 		 * @param {tinymce.ui.Container} container Container instance to preRender.
27800 		 */
27801 		preRender: function(container) {
27802 			container.addClass(this.settings.containerClass, 'body');
27803 		},
27804 
27805 		/**
27806 		 * Applies layout classes to the container.
27807 		 *
27808 		 * @private
27809 		 */
27810 		applyClasses: function(container) {
27811 			var self = this, settings = self.settings, items, firstClass, lastClass;
27812 
27813 			items = container.items().filter(':visible');
27814 			firstClass = settings.firstControlClass;
27815 			lastClass = settings.lastControlClass;
27816 
27817 			items.each(function(item) {
27818 				item.removeClass(firstClass).removeClass(lastClass);
27819 
27820 				if (settings.controlClass) {
27821 					item.addClass(settings.controlClass);
27822 				}
27823 			});
27824 
27825 			items.eq(0).addClass(firstClass);
27826 			items.eq(-1).addClass(lastClass);
27827 		},
27828 
27829 		/**
27830 		 * Renders the specified container and any layout specific HTML.
27831 		 *
27832 		 * @method renderHtml
27833 		 * @param {tinymce.ui.Container} container Container to render HTML for.
27834 		 */
27835 		renderHtml: function(container) {
27836 			var self = this, settings = self.settings, items, html = '';
27837 
27838 			items = container.items();
27839 			items.eq(0).addClass(settings.firstControlClass);
27840 			items.eq(-1).addClass(settings.lastControlClass);
27841 
27842 			items.each(function(item) {
27843 				if (settings.controlClass) {
27844 					item.addClass(settings.controlClass);
27845 				}
27846 
27847 				html += item.renderHtml();
27848 			});
27849 
27850 			return html;
27851 		},
27852 
27853 		/**
27854 		 * Recalculates the positions of the controls in the specified container.
27855 		 *
27856 		 * @method recalc
27857 		 * @param {tinymce.ui.Container} container Container instance to recalc.
27858 		 */
27859 		recalc: function() {
27860 		},
27861 
27862 		/**
27863 		 * This method gets invoked after the layout renders the controls.
27864 		 *
27865 		 * @method postRender
27866 		 * @param {tinymce.ui.Container} container Container instance to postRender.
27867 		 */
27868 		postRender: function() {
27869 		}
27870 	});
27871 });
27872 
27873 // Included from: js/tinymce/classes/ui/AbsoluteLayout.js
27874 
27875 /**
27876  * AbsoluteLayout.js
27877  *
27878  * Copyright, Moxiecode Systems AB
27879  * Released under LGPL License.
27880  *
27881  * License: http://www.tinymce.com/license
27882  * Contributing: http://www.tinymce.com/contributing
27883  */
27884 
27885 /**
27886  * LayoutManager for absolute positioning. This layout manager is more of
27887  * a base class for other layouts but can be created and used directly.
27888  *
27889  * @-x-less AbsoluteLayout.less
27890  * @class tinymce.ui.AbsoluteLayout
27891  * @extends tinymce.ui.Layout
27892  */
27893 define("tinymce/ui/AbsoluteLayout", [
27894 	"tinymce/ui/Layout"
27895 ], function(Layout) {
27896 	"use strict";
27897 
27898 	return Layout.extend({
27899 		Defaults: {
27900 			containerClass: 'abs-layout',
27901 			controlClass: 'abs-layout-item'
27902 		},
27903 
27904 		/**
27905 		 * Recalculates the positions of the controls in the specified container.
27906 		 *
27907 		 * @method recalc
27908 		 * @param {tinymce.ui.Container} container Container instance to recalc.
27909 		 */
27910 		recalc: function(container) {
27911 			container.items().filter(':visible').each(function(ctrl) {
27912 				var settings = ctrl.settings;
27913 
27914 				ctrl.layoutRect({
27915 					x: settings.x,
27916 					y: settings.y,
27917 					w: settings.w,
27918 					h: settings.h
27919 				});
27920 
27921 				if (ctrl.recalc) {
27922 					ctrl.recalc();
27923 				}
27924 			});
27925 		},
27926 
27927 		/**
27928 		 * Renders the specified container and any layout specific HTML.
27929 		 *
27930 		 * @method renderHtml
27931 		 * @param {tinymce.ui.Container} container Container to render HTML for.
27932 		 */
27933 		renderHtml: function(container) {
27934 			return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container);
27935 		}
27936 	});
27937 });
27938 
27939 // Included from: js/tinymce/classes/ui/Tooltip.js
27940 
27941 /**
27942  * Tooltip.js
27943  *
27944  * Copyright, Moxiecode Systems AB
27945  * Released under LGPL License.
27946  *
27947  * License: http://www.tinymce.com/license
27948  * Contributing: http://www.tinymce.com/contributing
27949  */
27950 
27951 /**
27952  * Creates a tooltip instance.
27953  *
27954  * @-x-less ToolTip.less
27955  * @class tinymce.ui.ToolTip
27956  * @extends tinymce.ui.Control
27957  * @mixes tinymce.ui.Movable
27958  */
27959 define("tinymce/ui/Tooltip", [
27960 	"tinymce/ui/Control",
27961 	"tinymce/ui/Movable"
27962 ], function(Control, Movable) {
27963 	return Control.extend({
27964 		Mixins: [Movable],
27965 
27966 		Defaults: {
27967 			classes: 'widget tooltip tooltip-n'
27968 		},
27969 
27970 		/**
27971 		 * Sets/gets the current label text.
27972 		 *
27973 		 * @method text
27974 		 * @param {String} [text] New label text.
27975 		 * @return {String|tinymce.ui.Tooltip} Current text or current label instance.
27976 		 */
27977 		text: function(value) {
27978 			var self = this;
27979 
27980 			if (typeof(value) != "undefined") {
27981 				self._value = value;
27982 
27983 				if (self._rendered) {
27984 					self.getEl().lastChild.innerHTML = self.encode(value);
27985 				}
27986 
27987 				return self;
27988 			}
27989 
27990 			return self._value;
27991 		},
27992 
27993 		/**
27994 		 * Renders the control as a HTML string.
27995 		 *
27996 		 * @method renderHtml
27997 		 * @return {String} HTML representing the control.
27998 		 */
27999 		renderHtml: function() {
28000 			var self = this, prefix = self.classPrefix;
28001 
28002 			return (
28003 				'<div id="' + self._id + '" class="' + self.classes() + '" role="presentation">' +
28004 					'<div class="' + prefix + 'tooltip-arrow"></div>' +
28005 					'<div class="' + prefix + 'tooltip-inner">' + self.encode(self._text) + '</div>' +
28006 				'</div>'
28007 			);
28008 		},
28009 
28010 		/**
28011 		 * Repaints the control after a layout operation.
28012 		 *
28013 		 * @method repaint
28014 		 */
28015 		repaint: function() {
28016 			var self = this, style, rect;
28017 
28018 			style = self.getEl().style;
28019 			rect = self._layoutRect;
28020 
28021 			style.left = rect.x + 'px';
28022 			style.top = rect.y + 'px';
28023 			style.zIndex = 0xFFFF + 0xFFFF;
28024 		}
28025 	});
28026 });
28027 
28028 // Included from: js/tinymce/classes/ui/Widget.js
28029 
28030 /**
28031  * Widget.js
28032  *
28033  * Copyright, Moxiecode Systems AB
28034  * Released under LGPL License.
28035  *
28036  * License: http://www.tinymce.com/license
28037  * Contributing: http://www.tinymce.com/contributing
28038  */
28039 
28040 /**
28041  * Widget base class a widget is a control that has a tooltip and some basic states.
28042  *
28043  * @class tinymce.ui.Widget
28044  * @extends tinymce.ui.Control
28045  */
28046 define("tinymce/ui/Widget", [
28047 	"tinymce/ui/Control",
28048 	"tinymce/ui/Tooltip"
28049 ], function(Control, Tooltip) {
28050 	"use strict";
28051 
28052 	var tooltip;
28053 
28054 	var Widget = Control.extend({
28055 		/**
28056 		 * Constructs a instance with the specified settings.
28057 		 *
28058 		 * @constructor
28059 		 * @param {Object} settings Name/value object with settings.
28060 		 * @setting {String} tooltip Tooltip text to display when hovering.
28061 		 * @setting {Boolean} autofocus True if the control should be focused when rendered.
28062 		 * @setting {String} text Text to display inside widget.
28063 		 */
28064 		init: function(settings) {
28065 			var self = this;
28066 
28067 			self._super(settings);
28068 			settings = self.settings;
28069 			self.canFocus = true;
28070 
28071 			if (settings.tooltip && Widget.tooltips !== false) {
28072 				self.on('mouseenter', function(e) {
28073 					var tooltip = self.tooltip().moveTo(-0xFFFF);
28074 
28075 					if (e.control == self) {
28076 						var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']);
28077 
28078 						tooltip.toggleClass('tooltip-n', rel == 'bc-tc');
28079 						tooltip.toggleClass('tooltip-nw', rel == 'bc-tl');
28080 						tooltip.toggleClass('tooltip-ne', rel == 'bc-tr');
28081 
28082 						tooltip.moveRel(self.getEl(), rel);
28083 					} else {
28084 						tooltip.hide();
28085 					}
28086 				});
28087 
28088 				self.on('mouseleave mousedown click', function() {
28089 					self.tooltip().hide();
28090 				});
28091 			}
28092 
28093 			self.aria('label', settings.ariaLabel || settings.tooltip);
28094 		},
28095 
28096 		/**
28097 		 * Returns the current tooltip instance.
28098 		 *
28099 		 * @method tooltip
28100 		 * @return {tinymce.ui.Tooltip} Tooltip instance.
28101 		 */
28102 		tooltip: function() {
28103 			if (!tooltip) {
28104 				tooltip = new Tooltip({type: 'tooltip'});
28105 				tooltip.renderTo();
28106 			}
28107 
28108 			return tooltip;
28109 		},
28110 
28111 		/**
28112 		 * Sets/gets the active state of the widget.
28113 		 *
28114 		 * @method active
28115 		 * @param {Boolean} [state] State if the control is active.
28116 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
28117 		 */
28118 		active: function(state) {
28119 			var self = this, undef;
28120 
28121 			if (state !== undef) {
28122 				self.aria('pressed', state);
28123 				self.toggleClass('active', state);
28124 			}
28125 
28126 			return self._super(state);
28127 		},
28128 
28129 		/**
28130 		 * Sets/gets the disabled state of the widget.
28131 		 *
28132 		 * @method disabled
28133 		 * @param {Boolean} [state] State if the control is disabled.
28134 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
28135 		 */
28136 		disabled: function(state) {
28137 			var self = this, undef;
28138 
28139 			if (state !== undef) {
28140 				self.aria('disabled', state);
28141 				self.toggleClass('disabled', state);
28142 			}
28143 
28144 			return self._super(state);
28145 		},
28146 
28147 		/**
28148 		 * Called after the control has been rendered.
28149 		 *
28150 		 * @method postRender
28151 		 */
28152 		postRender: function() {
28153 			var self = this, settings = self.settings;
28154 
28155 			self._rendered = true;
28156 
28157 			self._super();
28158 
28159 			if (!self.parent() && (settings.width || settings.height)) {
28160 				self.initLayoutRect();
28161 				self.repaint();
28162 			}
28163 
28164 			if (settings.autofocus) {
28165 				self.focus();
28166 			}
28167 		},
28168 
28169 		/**
28170 		 * Removes the current control from DOM and from UI collections.
28171 		 *
28172 		 * @method remove
28173 		 * @return {tinymce.ui.Control} Current control instance.
28174 		 */
28175 		remove: function() {
28176 			this._super();
28177 
28178 			if (tooltip) {
28179 				tooltip.remove();
28180 				tooltip = null;
28181 			}
28182 		}
28183 	});
28184 
28185 	return Widget;
28186 });
28187 
28188 // Included from: js/tinymce/classes/ui/Button.js
28189 
28190 /**
28191  * Button.js
28192  *
28193  * Copyright, Moxiecode Systems AB
28194  * Released under LGPL License.
28195  *
28196  * License: http://www.tinymce.com/license
28197  * Contributing: http://www.tinymce.com/contributing
28198  */
28199 
28200 /**
28201  * This class is used to create buttons. You can create them directly or through the Factory.
28202  *
28203  * @example
28204  * // Create and render a button to the body element
28205  * tinymce.ui.Factory.create({
28206  *     type: 'button',
28207  *     text: 'My button'
28208  * }).renderTo(document.body);
28209  *
28210  * @-x-less Button.less
28211  * @class tinymce.ui.Button
28212  * @extends tinymce.ui.Widget
28213  */
28214 define("tinymce/ui/Button", [
28215 	"tinymce/ui/Widget"
28216 ], function(Widget) {
28217 	"use strict";
28218 
28219 	return Widget.extend({
28220 		Defaults: {
28221 			classes: "widget btn",
28222 			role: "button"
28223 		},
28224 
28225 		/**
28226 		 * Constructs a new button instance with the specified settings.
28227 		 *
28228 		 * @constructor
28229 		 * @param {Object} settings Name/value object with settings.
28230 		 * @setting {String} size Size of the button small|medium|large.
28231 		 * @setting {String} image Image to use for icon.
28232 		 * @setting {String} icon Icon to use for button.
28233 		 */
28234 		init: function(settings) {
28235 			var self = this, size;
28236 
28237 			self.on('click mousedown', function(e) {
28238 				e.preventDefault();
28239 			});
28240 
28241 			self._super(settings);
28242 			size = settings.size;
28243 
28244 			if (settings.subtype) {
28245 				self.addClass(settings.subtype);
28246 			}
28247 
28248 			if (size) {
28249 				self.addClass('btn-' + size);
28250 			}
28251 		},
28252 
28253 		/**
28254 		 * Sets/gets the current button icon.
28255 		 *
28256 		 * @method icon
28257 		 * @param {String} [icon] New icon identifier.
28258 		 * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance.
28259 		 */
28260 		icon: function(icon) {
28261 			var self = this, prefix = self.classPrefix;
28262 
28263 			if (typeof(icon) == 'undefined') {
28264 				return self.settings.icon;
28265 			}
28266 
28267 			self.settings.icon = icon;
28268 			icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
28269 
28270 			if (self._rendered) {
28271 				var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
28272 
28273 				if (icon) {
28274 					if (!iconElm || iconElm != btnElm.firstChild) {
28275 						iconElm = document.createElement('i');
28276 						btnElm.insertBefore(iconElm, btnElm.firstChild);
28277 					}
28278 
28279 					iconElm.className = icon;
28280 				} else if (iconElm) {
28281 					btnElm.removeChild(iconElm);
28282 				}
28283 
28284 				self.text(self._text); // Set text again to fix whitespace between icon + text
28285 			}
28286 
28287 			return self;
28288 		},
28289 
28290 		/**
28291 		 * Repaints the button for example after it's been resizes by a layout engine.
28292 		 *
28293 		 * @method repaint
28294 		 */
28295 		repaint: function() {
28296 			var btnStyle = this.getEl().firstChild.style;
28297 
28298 			btnStyle.width = btnStyle.height = "100%";
28299 
28300 			this._super();
28301 		},
28302 
28303 		/**
28304 		 * Sets/gets the current button text.
28305 		 *
28306 		 * @method text
28307 		 * @param {String} [text] New button text.
28308 		 * @return {String|tinymce.ui.Button} Current text or current Button instance.
28309 		 */
28310 		text: function(text) {
28311 			var self = this;
28312 
28313 			if (self._rendered) {
28314 				var textNode = self.getEl().lastChild.lastChild;
28315 				if (textNode) {
28316 					textNode.data = self.translate(text);
28317 				}
28318 			}
28319 
28320 			return self._super(text);
28321 		},
28322 
28323 		/**
28324 		 * Renders the control as a HTML string.
28325 		 *
28326 		 * @method renderHtml
28327 		 * @return {String} HTML representing the control.
28328 		 */
28329 		renderHtml: function() {
28330 			var self = this, id = self._id, prefix = self.classPrefix;
28331 			var icon = self.settings.icon, image;
28332 
28333 			image = self.settings.image;
28334 			if (image) {
28335 				icon = 'none';
28336 
28337 				// Support for [high dpi, low dpi] image sources
28338 				if (typeof image != "string") {
28339 					image = window.getSelection ? image[0] : image[1];
28340 				}
28341 
28342 				image = ' style="background-image: url(\'' + image + '\')"';
28343 			} else {
28344 				image = '';
28345 			}
28346 
28347 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
28348 
28349 			return (
28350 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
28351 					'<button role="presentation" type="button" tabindex="-1">' +
28352 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
28353 						(self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') +
28354 					'</button>' +
28355 				'</div>'
28356 			);
28357 		}
28358 	});
28359 });
28360 
28361 // Included from: js/tinymce/classes/ui/ButtonGroup.js
28362 
28363 /**
28364  * ButtonGroup.js
28365  *
28366  * Copyright, Moxiecode Systems AB
28367  * Released under LGPL License.
28368  *
28369  * License: http://www.tinymce.com/license
28370  * Contributing: http://www.tinymce.com/contributing
28371  */
28372 
28373 /**
28374  * This control enables you to put multiple buttons into a group. This is
28375  * useful when you want to combine similar toolbar buttons into a group.
28376  *
28377  * @example
28378  * // Create and render a buttongroup with two buttons to the body element
28379  * tinymce.ui.Factory.create({
28380  *     type: 'buttongroup',
28381  *     items: [
28382  *         {text: 'Button A'},
28383  *         {text: 'Button B'}
28384  *     ]
28385  * }).renderTo(document.body);
28386  *
28387  * @-x-less ButtonGroup.less
28388  * @class tinymce.ui.ButtonGroup
28389  * @extends tinymce.ui.Container
28390  */
28391 define("tinymce/ui/ButtonGroup", [
28392 	"tinymce/ui/Container"
28393 ], function(Container) {
28394 	"use strict";
28395 
28396 	return Container.extend({
28397 		Defaults: {
28398 			defaultType: 'button',
28399 			role: 'group'
28400 		},
28401 
28402 		/**
28403 		 * Renders the control as a HTML string.
28404 		 *
28405 		 * @method renderHtml
28406 		 * @return {String} HTML representing the control.
28407 		 */
28408 		renderHtml: function() {
28409 			var self = this, layout = self._layout;
28410 
28411 			self.addClass('btn-group');
28412 			self.preRender();
28413 			layout.preRender(self);
28414 
28415 			return (
28416 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
28417 					'<div id="' + self._id + '-body">' +
28418 						(self.settings.html || '') + layout.renderHtml(self) +
28419 					'</div>' +
28420 				'</div>'
28421 			);
28422 		}
28423 	});
28424 });
28425 
28426 // Included from: js/tinymce/classes/ui/Checkbox.js
28427 
28428 /**
28429  * Checkbox.js
28430  *
28431  * Copyright, Moxiecode Systems AB
28432  * Released under LGPL License.
28433  *
28434  * License: http://www.tinymce.com/license
28435  * Contributing: http://www.tinymce.com/contributing
28436  */
28437 
28438 /**
28439  * This control creates a custom checkbox.
28440  *
28441  * @example
28442  * // Create and render a checkbox to the body element
28443  * tinymce.ui.Factory.create({
28444  *     type: 'checkbox',
28445  *     checked: true,
28446  *     text: 'My checkbox'
28447  * }).renderTo(document.body);
28448  *
28449  * @-x-less Checkbox.less
28450  * @class tinymce.ui.Checkbox
28451  * @extends tinymce.ui.Widget
28452  */
28453 define("tinymce/ui/Checkbox", [
28454 	"tinymce/ui/Widget"
28455 ], function(Widget) {
28456 	"use strict";
28457 
28458 	return Widget.extend({
28459 		Defaults: {
28460 			classes: "checkbox",
28461 			role: "checkbox",
28462 			checked: false
28463 		},
28464 
28465 		/**
28466 		 * Constructs a new Checkbox instance with the specified settings.
28467 		 *
28468 		 * @constructor
28469 		 * @param {Object} settings Name/value object with settings.
28470 		 * @setting {Boolean} checked True if the checkbox should be checked by default.
28471 		 */
28472 		init: function(settings) {
28473 			var self = this;
28474 
28475 			self._super(settings);
28476 
28477 			self.on('click mousedown', function(e) {
28478 				e.preventDefault();
28479 			});
28480 
28481 			self.on('click', function(e) {
28482 				e.preventDefault();
28483 
28484 				if (!self.disabled()) {
28485 					self.checked(!self.checked());
28486 				}
28487 			});
28488 
28489 			self.checked(self.settings.checked);
28490 		},
28491 
28492 		/**
28493 		 * Getter/setter function for the checked state.
28494 		 *
28495 		 * @method checked
28496 		 * @param {Boolean} [state] State to be set.
28497 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
28498 		 */
28499 		checked: function(state) {
28500 			var self = this;
28501 
28502 			if (typeof state != "undefined") {
28503 				if (state) {
28504 					self.addClass('checked');
28505 				} else {
28506 					self.removeClass('checked');
28507 				}
28508 
28509 				self._checked = state;
28510 				self.aria('checked', state);
28511 
28512 				return self;
28513 			}
28514 
28515 			return self._checked;
28516 		},
28517 
28518 		/**
28519 		 * Getter/setter function for the value state.
28520 		 *
28521 		 * @method value
28522 		 * @param {Boolean} [state] State to be set.
28523 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
28524 		 */
28525 		value: function(state) {
28526 			return this.checked(state);
28527 		},
28528 
28529 		/**
28530 		 * Renders the control as a HTML string.
28531 		 *
28532 		 * @method renderHtml
28533 		 * @return {String} HTML representing the control.
28534 		 */
28535 		renderHtml: function() {
28536 			var self = this, id = self._id, prefix = self.classPrefix;
28537 
28538 			return (
28539 				'<div id="' + id + '" class="' + self.classes() + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' +
28540 					'<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' +
28541 					'<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self._text) + '</span>' +
28542 				'</div>'
28543 			);
28544 		}
28545 	});
28546 });
28547 
28548 // Included from: js/tinymce/classes/ui/PanelButton.js
28549 
28550 /**
28551  * PanelButton.js
28552  *
28553  * Copyright, Moxiecode Systems AB
28554  * Released under LGPL License.
28555  *
28556  * License: http://www.tinymce.com/license
28557  * Contributing: http://www.tinymce.com/contributing
28558  */
28559 
28560 /**
28561  * Creates a new panel button.
28562  *
28563  * @class tinymce.ui.PanelButton
28564  * @extends tinymce.ui.Button
28565  */
28566 define("tinymce/ui/PanelButton", [
28567 	"tinymce/ui/Button",
28568 	"tinymce/ui/FloatPanel"
28569 ], function(Button, FloatPanel) {
28570 	"use strict";
28571 
28572 	return Button.extend({
28573 		/**
28574 		 * Shows the panel for the button.
28575 		 *
28576 		 * @method showPanel
28577 		 */
28578 		showPanel: function() {
28579 			var self = this, settings = self.settings;
28580 
28581 			self.active(true);
28582 
28583 			if (!self.panel) {
28584 				var panelSettings = settings.panel;
28585 
28586 				// Wrap panel in grid layout if type if specified
28587 				// This makes it possible to add forms or other containers directly in the panel option
28588 				if (panelSettings.type) {
28589 					panelSettings = {
28590 						layout: 'grid',
28591 						items: panelSettings
28592 					};
28593 				}
28594 
28595 				panelSettings.role = panelSettings.role || 'dialog';
28596 				panelSettings.popover = true;
28597 				panelSettings.autohide = true;
28598 				panelSettings.ariaRoot = true;
28599 
28600 				self.panel = new FloatPanel(panelSettings).on('hide', function() {
28601 					self.active(false);
28602 				}).on('cancel', function(e) {
28603 					e.stopPropagation();
28604 					self.focus();
28605 					self.hidePanel();
28606 				}).parent(self).renderTo(self.getContainerElm());
28607 
28608 				self.panel.fire('show');
28609 				self.panel.reflow();
28610 			} else {
28611 				self.panel.show();
28612 			}
28613 
28614 			self.panel.moveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tr', 'bc-tc'] : ['bc-tl', 'bc-tc']));
28615 		},
28616 
28617 		/**
28618 		 * Hides the panel for the button.
28619 		 *
28620 		 * @method hidePanel
28621 		 */
28622 		hidePanel: function() {
28623 			var self = this;
28624 
28625 			if (self.panel) {
28626 				self.panel.hide();
28627 			}
28628 		},
28629 
28630 		/**
28631 		 * Called after the control has been rendered.
28632 		 *
28633 		 * @method postRender
28634 		 */
28635 		postRender: function() {
28636 			var self = this;
28637 
28638 			self.aria('haspopup', true);
28639 
28640 			self.on('click', function(e) {
28641 				if (e.control === self) {
28642 					if (self.panel && self.panel.visible()) {
28643 						self.hidePanel();
28644 					} else {
28645 						self.showPanel();
28646 						self.panel.focus(!!e.aria);
28647 					}
28648 				}
28649 			});
28650 
28651 			return self._super();
28652 		}
28653 	});
28654 });
28655 
28656 // Included from: js/tinymce/classes/ui/ColorButton.js
28657 
28658 /**
28659  * ColorButton.js
28660  *
28661  * Copyright, Moxiecode Systems AB
28662  * Released under LGPL License.
28663  *
28664  * License: http://www.tinymce.com/license
28665  * Contributing: http://www.tinymce.com/contributing
28666  */
28667 
28668 /**
28669  * This class creates a color button control. This is a split button in which the main
28670  * button has a visual representation of the currently selected color. When clicked 
28671  * the caret button displays a color picker, allowing the user to select a new color.
28672  *
28673  * @-x-less ColorButton.less
28674  * @class tinymce.ui.ColorButton
28675  * @extends tinymce.ui.PanelButton
28676  */
28677 define("tinymce/ui/ColorButton", [
28678 	"tinymce/ui/PanelButton",
28679 	"tinymce/dom/DOMUtils"
28680 ], function(PanelButton, DomUtils) {
28681 	"use strict";
28682 	
28683 	var DOM = DomUtils.DOM;
28684 
28685 	return PanelButton.extend({
28686 		/**
28687 		 * Constructs a new ColorButton instance with the specified settings.
28688 		 *
28689 		 * @constructor
28690 		 * @param {Object} settings Name/value object with settings.
28691 		 */
28692 		init: function(settings) {
28693 			this._super(settings);
28694 			this.addClass('colorbutton');
28695 		},
28696 
28697 		/**
28698 		 * Getter/setter for the current color.
28699 		 *
28700 		 * @method color
28701 		 * @param {String} [color] Color to set.
28702 		 * @return {String|tinymce.ui.ColorButton} Current color or current instance.
28703 		 */
28704 		color: function(color) {
28705 			if (color) {
28706 				this._color = color;
28707 				this.getEl('preview').style.backgroundColor = color;
28708 				return this;
28709 			}
28710 
28711 			return this._color;
28712 		},
28713 
28714 		/**
28715 		 * Renders the control as a HTML string.
28716 		 *
28717 		 * @method renderHtml
28718 		 * @return {String} HTML representing the control.
28719 		 */
28720 		renderHtml: function() {
28721 			var self = this, id = self._id, prefix = self.classPrefix;
28722 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
28723 			var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '';
28724 
28725 			return (
28726 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1" aria-haspopup="true">' +
28727 					'<button role="presentation" hidefocus="1" type="button" tabindex="-1">' +
28728 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
28729 						'<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' +
28730 						(self._text ? (icon ? ' ' : '') + (self._text) : '') +
28731 					'</button>' +
28732 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
28733 						' <i class="' + prefix + 'caret"></i>' +
28734 					'</button>' +
28735 				'</div>'
28736 			);
28737 		},
28738 		
28739 		/**
28740 		 * Called after the control has been rendered.
28741 		 *
28742 		 * @method postRender
28743 		 */
28744 		postRender: function() {
28745 			var self = this, onClickHandler = self.settings.onclick;
28746 
28747 			self.on('click', function(e) {
28748 				if (e.aria && e.aria.key == 'down') {
28749 					return;
28750 				}
28751 
28752 				if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) {
28753 					e.stopImmediatePropagation();
28754 					onClickHandler.call(self, e);
28755 				}
28756 			});
28757 
28758 			delete self.settings.onclick;
28759 
28760 			return self._super();
28761 		}
28762 		
28763 	});
28764 });
28765 
28766 // Included from: js/tinymce/classes/ui/ComboBox.js
28767 
28768 /**
28769  * ComboBox.js
28770  *
28771  * Copyright, Moxiecode Systems AB
28772  * Released under LGPL License.
28773  *
28774  * License: http://www.tinymce.com/license
28775  * Contributing: http://www.tinymce.com/contributing
28776  */
28777 
28778 /**
28779  * This class creates a combobox control. Select box that you select a value from or
28780  * type a value into.
28781  *
28782  * @-x-less ComboBox.less
28783  * @class tinymce.ui.ComboBox
28784  * @extends tinymce.ui.Widget
28785  */
28786 define("tinymce/ui/ComboBox", [
28787 	"tinymce/ui/Widget",
28788 	"tinymce/ui/Factory",
28789 	"tinymce/ui/DomUtils"
28790 ], function(Widget, Factory, DomUtils) {
28791 	"use strict";
28792 
28793 	return Widget.extend({
28794 		/**
28795 		 * Constructs a new control instance with the specified settings.
28796 		 *
28797 		 * @constructor
28798 		 * @param {Object} settings Name/value object with settings.
28799 		 * @setting {String} placeholder Placeholder text to display.
28800 		 */
28801 		init: function(settings) {
28802 			var self = this;
28803 
28804 			self._super(settings);
28805 			self.addClass('combobox');
28806 			self.subinput = true;
28807 			self.ariaTarget = 'inp'; // TODO: Figure out a better way
28808 
28809 			settings = self.settings;
28810 			settings.menu = settings.menu || settings.values;
28811 
28812 			if (settings.menu) {
28813 				settings.icon = 'caret';
28814 			}
28815 
28816 			self.on('click', function(e) {
28817 				var elm = e.target, root = self.getEl();
28818 
28819 				while (elm && elm != root) {
28820 					if (elm.id && elm.id.indexOf('-open') != -1) {
28821 						self.fire('action');
28822 
28823 						if (settings.menu) {
28824 							self.showMenu();
28825 
28826 							if (e.aria) {
28827 								self.menu.items()[0].focus();
28828 							}
28829 						}
28830 					}
28831 
28832 					elm = elm.parentNode;
28833 				}
28834 			});
28835 
28836 			// TODO: Rework this
28837 			self.on('keydown', function(e) {
28838 				if (e.target.nodeName == "INPUT" && e.keyCode == 13) {
28839 					self.parents().reverse().each(function(ctrl) {
28840 						e.preventDefault();
28841 						self.fire('change');
28842 
28843 						if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
28844 							ctrl.fire('submit', {data: ctrl.toJSON()});
28845 							return false;
28846 						}
28847 					});
28848 				}
28849 			});
28850 
28851 			if (settings.placeholder) {
28852 				self.addClass('placeholder');
28853 
28854 				self.on('focusin', function() {
28855 					if (!self._hasOnChange) {
28856 						DomUtils.on(self.getEl('inp'), 'change', function() {
28857 							self.fire('change');
28858 						});
28859 
28860 						self._hasOnChange = true;
28861 					}
28862 
28863 					if (self.hasClass('placeholder')) {
28864 						self.getEl('inp').value = '';
28865 						self.removeClass('placeholder');
28866 					}
28867 				});
28868 
28869 				self.on('focusout', function() {
28870 					if (self.value().length === 0) {
28871 						self.getEl('inp').value = settings.placeholder;
28872 						self.addClass('placeholder');
28873 					}
28874 				});
28875 			}
28876 		},
28877 
28878 		showMenu: function() {
28879 			var self = this, settings = self.settings, menu;
28880 
28881 			if (!self.menu) {
28882 				menu = settings.menu || [];
28883 
28884 				// Is menu array then auto constuct menu control
28885 				if (menu.length) {
28886 					menu = {
28887 						type: 'menu',
28888 						items: menu
28889 					};
28890 				} else {
28891 					menu.type = menu.type || 'menu';
28892 				}
28893 
28894 				self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
28895 				self.fire('createmenu');
28896 				self.menu.reflow();
28897 				self.menu.on('cancel', function(e) {
28898 					if (e.control === self.menu) {
28899 						self.focus();
28900 					}
28901 				});
28902 
28903 				self.menu.on('show hide', function(e) {
28904 					e.control.items().each(function(ctrl) {
28905 						ctrl.active(ctrl.value() == self.value());
28906 					});
28907 				}).fire('show');
28908 
28909 				self.menu.on('select', function(e) {
28910 					self.value(e.control.value());
28911 				});
28912 
28913 				self.on('focusin', function(e) {
28914 					if (e.target.tagName.toUpperCase() == 'INPUT') {
28915 						self.menu.hide();
28916 					}
28917 				});
28918 
28919 				self.aria('expanded', true);
28920 			}
28921 
28922 			self.menu.show();
28923 			self.menu.layoutRect({w: self.layoutRect().w});
28924 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
28925 		},
28926 
28927 		/**
28928 		 * Getter/setter function for the control value.
28929 		 *
28930 		 * @method value
28931 		 * @param {String} [value] Value to be set.
28932 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
28933 		 */
28934 		value: function(value) {
28935 			var self = this;
28936 
28937 			if (typeof(value) != "undefined") {
28938 				self._value = value;
28939 				self.removeClass('placeholder');
28940 
28941 				if (self._rendered) {
28942 					self.getEl('inp').value = value;
28943 				}
28944 
28945 				return self;
28946 			}
28947 
28948 			if (self._rendered) {
28949 				value = self.getEl('inp').value;
28950 
28951 				if (value != self.settings.placeholder) {
28952 					return value;
28953 				}
28954 
28955 				return '';
28956 			}
28957 
28958 			return self._value;
28959 		},
28960 
28961 		/**
28962 		 * Getter/setter function for the disabled state.
28963 		 *
28964 		 * @method value
28965 		 * @param {Boolean} [state] State to be set.
28966 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
28967 		 */
28968 		disabled: function(state) {
28969 			var self = this;
28970 
28971 			if (self._rendered && typeof(state) != 'undefined') {
28972 				self.getEl('inp').disabled = state;
28973 			}
28974 
28975 			return self._super(state);
28976 		},
28977 
28978 		/**
28979 		 * Focuses the input area of the control.
28980 		 *
28981 		 * @method focus
28982 		 */
28983 		focus: function() {
28984 			this.getEl('inp').focus();
28985 		},
28986 
28987 		/**
28988 		 * Repaints the control after a layout operation.
28989 		 *
28990 		 * @method repaint
28991 		 */
28992 		repaint: function() {
28993 			var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect();
28994 			var width, lineHeight;
28995 
28996 			if (openElm) {
28997 				width = rect.w - DomUtils.getSize(openElm).width - 10;
28998 			} else {
28999 				width = rect.w - 10;
29000 			}
29001 
29002 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
29003 			var doc = document;
29004 			if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
29005 				lineHeight = (self.layoutRect().h - 2) + 'px';
29006 			}
29007 
29008 			DomUtils.css(elm.firstChild, {
29009 				width: width,
29010 				lineHeight: lineHeight
29011 			});
29012 
29013 			self._super();
29014 
29015 			return self;
29016 		},
29017 
29018 		/**
29019 		 * Post render method. Called after the control has been rendered to the target.
29020 		 *
29021 		 * @method postRender
29022 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
29023 		 */
29024 		postRender: function() {
29025 			var self = this;
29026 
29027 			DomUtils.on(this.getEl('inp'), 'change', function() {
29028 				self.fire('change');
29029 			});
29030 
29031 			return self._super();
29032 		},
29033 
29034 		remove: function() {
29035 			DomUtils.off(this.getEl('inp'));
29036 			this._super();
29037 		},
29038 
29039 		/**
29040 		 * Renders the control as a HTML string.
29041 		 *
29042 		 * @method renderHtml
29043 		 * @return {String} HTML representing the control.
29044 		 */
29045 		renderHtml: function() {
29046 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
29047 			var value = settings.value || settings.placeholder || '';
29048 			var icon, text, openBtnHtml = '', extraAttrs = '';
29049 
29050 			if ("spellcheck" in settings) {
29051 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
29052 			}
29053 
29054 			if (settings.maxLength) {
29055 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
29056 			}
29057 
29058 			if (settings.size) {
29059 				extraAttrs += ' size="' + settings.size + '"';
29060 			}
29061 
29062 			if (settings.subtype) {
29063 				extraAttrs += ' type="' + settings.subtype + '"';
29064 			}
29065 
29066 			if (self.disabled()) {
29067 				extraAttrs += ' disabled="disabled"';
29068 			}
29069 
29070 			icon = settings.icon;
29071 			if (icon && icon != 'caret') {
29072 				icon = prefix + 'ico ' + prefix + 'i-' + settings.icon;
29073 			}
29074 
29075 			text = self._text;
29076 
29077 			if (icon || text) {
29078 				openBtnHtml = (
29079 					'<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' +
29080 						'<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' +
29081 							(icon != 'caret' ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') +
29082 							(text ? (icon ? ' ' : '') + text : '') +
29083 						'</button>' +
29084 					'</div>'
29085 				);
29086 
29087 				self.addClass('has-open');
29088 			}
29089 
29090 			return (
29091 				'<div id="' + id + '" class="' + self.classes() + '">' +
29092 					'<input id="' + id + '-inp" class="' + prefix + 'textbox ' + prefix + 'placeholder" value="' +
29093 					value + '" hidefocus="1"' + extraAttrs + ' />' +
29094 					openBtnHtml +
29095 				'</div>'
29096 			);
29097 		}
29098 	});
29099 });
29100 
29101 // Included from: js/tinymce/classes/ui/Path.js
29102 
29103 /**
29104  * Path.js
29105  *
29106  * Copyright, Moxiecode Systems AB
29107  * Released under LGPL License.
29108  *
29109  * License: http://www.tinymce.com/license
29110  * Contributing: http://www.tinymce.com/contributing
29111  */
29112 
29113 /**
29114  * Creates a new path control.
29115  *
29116  * @-x-less Path.less
29117  * @class tinymce.ui.Path
29118  * @extends tinymce.ui.Widget
29119  */
29120 define("tinymce/ui/Path", [
29121 	"tinymce/ui/Widget"
29122 ], function(Widget) {
29123 	"use strict";
29124 
29125 	return Widget.extend({
29126 		/**
29127 		 * Constructs a instance with the specified settings.
29128 		 *
29129 		 * @constructor
29130 		 * @param {Object} settings Name/value object with settings.
29131 		 * @setting {String} delimiter Delimiter to display between items in path.
29132 		 */
29133 		init: function(settings) {
29134 			var self = this;
29135 
29136 			if (!settings.delimiter) {
29137 				settings.delimiter = '\u00BB';
29138 			}
29139 
29140 			self._super(settings);
29141 			self.addClass('path');
29142 			self.canFocus = true;
29143 
29144 			self.on('click', function(e) {
29145 				var index, target = e.target;
29146 
29147 				if ((index = target.getAttribute('data-index'))) {
29148 					self.fire('select', {value: self.data()[index], index: index});
29149 				}
29150 			});
29151 		},
29152 
29153 		/**
29154 		 * Focuses the current control.
29155 		 *
29156 		 * @method focus
29157 		 * @return {tinymce.ui.Control} Current control instance.
29158 		 */
29159 		focus: function() {
29160 			var self = this;
29161 
29162 			self.getEl().firstChild.focus();
29163 
29164 			return self;
29165 		},
29166 
29167 		/**
29168 		 * Sets/gets the data to be used for the path.
29169 		 *
29170 		 * @method data
29171 		 * @param {Array} data Array with items name is rendered to path.
29172 		 */
29173 		data: function(data) {
29174 			var self = this;
29175 
29176 			if (typeof(data) !== "undefined") {
29177 				self._data = data;
29178 				self.update();
29179 
29180 				return self;
29181 			}
29182 
29183 			return self._data;
29184 		},
29185 
29186 		/**
29187 		 * Updated the path.
29188 		 *
29189 		 * @private
29190 		 */
29191 		update: function() {
29192 			this.innerHtml(this._getPathHtml());
29193 		},
29194 
29195 		/**
29196 		 * Called after the control has been rendered.
29197 		 *
29198 		 * @method postRender
29199 		 */
29200 		postRender: function() {
29201 			var self = this;
29202 
29203 			self._super();
29204 
29205 			self.data(self.settings.data);
29206 		},
29207 
29208 		/**
29209 		 * Renders the control as a HTML string.
29210 		 *
29211 		 * @method renderHtml
29212 		 * @return {String} HTML representing the control.
29213 		 */
29214 		renderHtml: function() {
29215 			var self = this;
29216 
29217 			return (
29218 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
29219 					self._getPathHtml() +
29220 				'</div>'
29221 			);
29222 		},
29223 
29224 		_getPathHtml: function() {
29225 			var self = this, parts = self._data || [], i, l, html = '', prefix = self.classPrefix;
29226 
29227 			for (i = 0, l = parts.length; i < l; i++) {
29228 				html += (
29229 					(i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') +
29230 					'<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' +
29231 					i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + i + '">' + parts[i].name + '</div>'
29232 				);
29233 			}
29234 
29235 			if (!html) {
29236 				html = '<div class="' + prefix + 'path-item">\u00a0</div>';
29237 			}
29238 
29239 			return html;
29240 		}
29241 	});
29242 });
29243 
29244 // Included from: js/tinymce/classes/ui/ElementPath.js
29245 
29246 /**
29247  * ElementPath.js
29248  *
29249  * Copyright, Moxiecode Systems AB
29250  * Released under LGPL License.
29251  *
29252  * License: http://www.tinymce.com/license
29253  * Contributing: http://www.tinymce.com/contributing
29254  */
29255 
29256 /**
29257  * This control creates an path for the current selections parent elements in TinyMCE.
29258  *
29259  * @class tinymce.ui.ElementPath
29260  * @extends tinymce.ui.Path
29261  */
29262 define("tinymce/ui/ElementPath", [
29263 	"tinymce/ui/Path",
29264 	"tinymce/EditorManager"
29265 ], function(Path, EditorManager) {
29266 	return Path.extend({
29267 		/**
29268 		 * Post render method. Called after the control has been rendered to the target.
29269 		 *
29270 		 * @method postRender
29271 		 * @return {tinymce.ui.ElementPath} Current combobox instance.
29272 		 */
29273 		postRender: function() {
29274 			var self = this, editor = EditorManager.activeEditor;
29275 
29276 			function isHidden(elm) {
29277 				if (elm.nodeType === 1) {
29278 					if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) {
29279 						return true;
29280 					}
29281 
29282 					if (elm.getAttribute('data-mce-type') === 'bookmark') {
29283 						return true;
29284 					}
29285 				}
29286 
29287 				return false;
29288 			}
29289 
29290 			self.on('select', function(e) {
29291 				var parents = [], node, body = editor.getBody();
29292 
29293 				editor.focus();
29294 
29295 				node = editor.selection.getStart();
29296 				while (node && node != body) {
29297 					if (!isHidden(node)) {
29298 						parents.push(node);
29299 					}
29300 
29301 					node = node.parentNode;
29302 				}
29303 
29304 				editor.selection.select(parents[parents.length - 1 - e.index]);
29305 				editor.nodeChanged();
29306 			});
29307 
29308 			editor.on('nodeChange', function(e) {
29309 				var parents = [], selectionParents = e.parents, i = selectionParents.length;
29310 
29311 				while (i--) {
29312 					if (selectionParents[i].nodeType == 1 && !isHidden(selectionParents[i])) {
29313 						var args = editor.fire('ResolveName', {
29314 							name: selectionParents[i].nodeName.toLowerCase(),
29315 							target: selectionParents[i]
29316 						});
29317 
29318 						parents.push({name: args.name});
29319 					}
29320 				}
29321 
29322 				self.data(parents);
29323 			});
29324 
29325 			return self._super();
29326 		}
29327 	});
29328 });
29329 
29330 // Included from: js/tinymce/classes/ui/FormItem.js
29331 
29332 /**
29333  * FormItem.js
29334  *
29335  * Copyright, Moxiecode Systems AB
29336  * Released under LGPL License.
29337  *
29338  * License: http://www.tinymce.com/license
29339  * Contributing: http://www.tinymce.com/contributing
29340  */
29341 
29342 /**
29343  * This class is a container created by the form element with
29344  * a label and control item.
29345  *
29346  * @class tinymce.ui.FormItem
29347  * @extends tinymce.ui.Container
29348  * @setting {String} label Label to display for the form item.
29349  */
29350 define("tinymce/ui/FormItem", [
29351 	"tinymce/ui/Container"
29352 ], function(Container) {
29353 	"use strict";
29354 
29355 	return Container.extend({
29356 		Defaults: {
29357 			layout: 'flex',
29358 			align: 'center',
29359 			defaults: {
29360 				flex: 1
29361 			}
29362 		},
29363 
29364 		/**
29365 		 * Renders the control as a HTML string.
29366 		 *
29367 		 * @method renderHtml
29368 		 * @return {String} HTML representing the control.
29369 		 */
29370 		renderHtml: function() {
29371 			var self = this, layout = self._layout, prefix = self.classPrefix;
29372 
29373 			self.addClass('formitem');
29374 			layout.preRender(self);
29375 
29376 			return (
29377 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
29378 					(self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' +
29379 						self.settings.title + '</div>') : '') +
29380 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
29381 						(self.settings.html || '') + layout.renderHtml(self) +
29382 					'</div>' +
29383 				'</div>'
29384 			);
29385 		}
29386 	});
29387 });
29388 
29389 // Included from: js/tinymce/classes/ui/Form.js
29390 
29391 /**
29392  * Form.js
29393  *
29394  * Copyright, Moxiecode Systems AB
29395  * Released under LGPL License.
29396  *
29397  * License: http://www.tinymce.com/license
29398  * Contributing: http://www.tinymce.com/contributing
29399  */
29400 
29401 /**
29402  * This class creates a form container. A form container has the ability
29403  * to automatically wrap items in tinymce.ui.FormItem instances.
29404  *
29405  * Each FormItem instance is a container for the label and the item.
29406  *
29407  * @example
29408  * tinymce.ui.Factory.create({
29409  *     type: 'form',
29410  *     items: [
29411  *         {type: 'textbox', label: 'My text box'}
29412  *     ]
29413  * }).renderTo(document.body);
29414  *
29415  * @class tinymce.ui.Form
29416  * @extends tinymce.ui.Container
29417  */
29418 define("tinymce/ui/Form", [
29419 	"tinymce/ui/Container",
29420 	"tinymce/ui/FormItem"
29421 ], function(Container, FormItem) {
29422 	"use strict";
29423 
29424 	return Container.extend({
29425 		Defaults: {
29426 			containerCls: 'form',
29427 			layout: 'flex',
29428 			direction: 'column',
29429 			align: 'stretch',
29430 			flex: 1,
29431 			padding: 20,
29432 			labelGap: 30,
29433 			spacing: 10,
29434 			callbacks: {
29435 				submit: function() {
29436 					this.submit();
29437 				}
29438 			}
29439 		},
29440 
29441 		/**
29442 		 * This method gets invoked before the control is rendered.
29443 		 *
29444 		 * @method preRender
29445 		 */
29446 		preRender: function() {
29447 			var self = this, items = self.items();
29448 
29449 			// Wrap any labeled items in FormItems
29450 			items.each(function(ctrl) {
29451 				var formItem, label = ctrl.settings.label;
29452 
29453 				if (label) {
29454 					formItem = new FormItem({
29455 						layout: 'flex',
29456 						autoResize: "overflow",
29457 						defaults: {flex: 1},
29458 						items: [
29459 							{type: 'label', id: ctrl._id + '-l', text: label, flex: 0, forId: ctrl._id, disabled: ctrl.disabled()}
29460 						]
29461 					});
29462 
29463 					formItem.type = 'formitem';
29464 					ctrl.aria('labelledby', ctrl._id + '-l');
29465 
29466 					if (typeof(ctrl.settings.flex) == "undefined") {
29467 						ctrl.settings.flex = 1;
29468 					}
29469 
29470 					self.replace(ctrl, formItem);
29471 					formItem.add(ctrl);
29472 				}
29473 			});
29474 		},
29475 
29476 		/**
29477 		 * Recalcs label widths.
29478 		 *
29479 		 * @private
29480 		 */
29481 		recalcLabels: function() {
29482 			var self = this, maxLabelWidth = 0, labels = [], i, labelGap;
29483 
29484 			if (self.settings.labelGapCalc === false) {
29485 				return;
29486 			}
29487 
29488 			self.items().filter('formitem').each(function(item) {
29489 				var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
29490 
29491 				maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
29492 				labels.push(labelCtrl);
29493 			});
29494 
29495 			labelGap = self.settings.labelGap || 0;
29496 
29497 			i = labels.length;
29498 			while (i--) {
29499 				labels[i].settings.minWidth = maxLabelWidth + labelGap;
29500 			}
29501 		},
29502 
29503 		/**
29504 		 * Getter/setter for the visibility state.
29505 		 *
29506 		 * @method visible
29507 		 * @param {Boolean} [state] True/false state to show/hide.
29508 		 * @return {tinymce.ui.Form|Boolean} True/false state or current control.
29509 		 */
29510 		visible: function(state) {
29511 			var val = this._super(state);
29512 
29513 			if (state === true && this._rendered) {
29514 				this.recalcLabels();
29515 			}
29516 
29517 			return val;
29518 		},
29519 
29520 		/**
29521 		 * Fires a submit event with the serialized form.
29522 		 *
29523 		 * @method submit
29524 		 * @return {Object} Event arguments object.
29525 		 */
29526 		submit: function() {
29527 			return this.fire('submit', {data: this.toJSON()});
29528 		},
29529 
29530 		/**
29531 		 * Post render method. Called after the control has been rendered to the target.
29532 		 *
29533 		 * @method postRender
29534 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
29535 		 */
29536 		postRender: function() {
29537 			var self = this;
29538 
29539 			self._super();
29540 			self.recalcLabels();
29541 			self.fromJSON(self.settings.data);
29542 		}
29543 	});
29544 });
29545 
29546 // Included from: js/tinymce/classes/ui/FieldSet.js
29547 
29548 /**
29549  * FieldSet.js
29550  *
29551  * Copyright, Moxiecode Systems AB
29552  * Released under LGPL License.
29553  *
29554  * License: http://www.tinymce.com/license
29555  * Contributing: http://www.tinymce.com/contributing
29556  */
29557 
29558 /**
29559  * This class creates fieldset containers.
29560  *
29561  * @-x-less FieldSet.less
29562  * @class tinymce.ui.FieldSet
29563  * @extends tinymce.ui.Form
29564  */
29565 define("tinymce/ui/FieldSet", [
29566 	"tinymce/ui/Form"
29567 ], function(Form) {
29568 	"use strict";
29569 
29570 	return Form.extend({
29571 		Defaults: {
29572 			containerCls: 'fieldset',
29573 			layout: 'flex',
29574 			direction: 'column',
29575 			align: 'stretch',
29576 			flex: 1,
29577 			padding: "25 15 5 15",
29578 			labelGap: 30,
29579 			spacing: 10,
29580 			border: 1
29581 		},
29582 
29583 		/**
29584 		 * Renders the control as a HTML string.
29585 		 *
29586 		 * @method renderHtml
29587 		 * @return {String} HTML representing the control.
29588 		 */
29589 		renderHtml: function() {
29590 			var self = this, layout = self._layout, prefix = self.classPrefix;
29591 
29592 			self.preRender();
29593 			layout.preRender(self);
29594 
29595 			return (
29596 				'<fieldset id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
29597 					(self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' +
29598 						self.settings.title + '</legend>') : '') +
29599 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
29600 						(self.settings.html || '') + layout.renderHtml(self) +
29601 					'</div>' +
29602 				'</fieldset>'
29603 			);
29604 		}
29605 	});
29606 });
29607 
29608 // Included from: js/tinymce/classes/ui/FilePicker.js
29609 
29610 /**
29611  * FilePicker.js
29612  *
29613  * Copyright, Moxiecode Systems AB
29614  * Released under LGPL License.
29615  *
29616  * License: http://www.tinymce.com/license
29617  * Contributing: http://www.tinymce.com/contributing
29618  */
29619 
29620 /*global tinymce:true */
29621 
29622 /**
29623  * This class creates a file picker control.
29624  *
29625  * @class tinymce.ui.FilePicker
29626  * @extends tinymce.ui.ComboBox
29627  */
29628 define("tinymce/ui/FilePicker", [
29629 	"tinymce/ui/ComboBox"
29630 ], function(ComboBox) {
29631 	"use strict";
29632 
29633 	return ComboBox.extend({
29634 		/**
29635 		 * Constructs a new control instance with the specified settings.
29636 		 *
29637 		 * @constructor
29638 		 * @param {Object} settings Name/value object with settings.
29639 		 */
29640 		init: function(settings) {
29641 			var self = this, editor = tinymce.activeEditor, fileBrowserCallback;
29642 
29643 			settings.spellcheck = false;
29644 
29645 			fileBrowserCallback = editor.settings.file_browser_callback;
29646 			if (fileBrowserCallback) {
29647 				settings.icon = 'browse';
29648 
29649 				settings.onaction = function() {
29650 					fileBrowserCallback(
29651 						self.getEl('inp').id,
29652 						self.getEl('inp').value,
29653 						settings.filetype,
29654 						window
29655 					);
29656 				};
29657 			}
29658 
29659 			self._super(settings);
29660 		}
29661 	});
29662 });
29663 
29664 // Included from: js/tinymce/classes/ui/FitLayout.js
29665 
29666 /**
29667  * FitLayout.js
29668  *
29669  * Copyright, Moxiecode Systems AB
29670  * Released under LGPL License.
29671  *
29672  * License: http://www.tinymce.com/license
29673  * Contributing: http://www.tinymce.com/contributing
29674  */
29675 
29676 /**
29677  * This layout manager will resize the control to be the size of it's parent container.
29678  * In other words width: 100% and height: 100%.
29679  *
29680  * @-x-less FitLayout.less
29681  * @class tinymce.ui.FitLayout
29682  * @extends tinymce.ui.AbsoluteLayout
29683  */
29684 define("tinymce/ui/FitLayout", [
29685 	"tinymce/ui/AbsoluteLayout"
29686 ], function(AbsoluteLayout) {
29687 	"use strict";
29688 
29689 	return AbsoluteLayout.extend({
29690 		/**
29691 		 * Recalculates the positions of the controls in the specified container.
29692 		 *
29693 		 * @method recalc
29694 		 * @param {tinymce.ui.Container} container Container instance to recalc.
29695 		 */
29696 		recalc: function(container) {
29697 			var contLayoutRect = container.layoutRect(), paddingBox = container.paddingBox();
29698 
29699 			container.items().filter(':visible').each(function(ctrl) {
29700 				ctrl.layoutRect({
29701 					x: paddingBox.left,
29702 					y: paddingBox.top,
29703 					w: contLayoutRect.innerW - paddingBox.right - paddingBox.left,
29704 					h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom
29705 				});
29706 
29707 				if (ctrl.recalc) {
29708 					ctrl.recalc();
29709 				}
29710 			});
29711 		}
29712 	});
29713 });
29714 
29715 // Included from: js/tinymce/classes/ui/FlexLayout.js
29716 
29717 /**
29718  * FlexLayout.js
29719  *
29720  * Copyright, Moxiecode Systems AB
29721  * Released under LGPL License.
29722  *
29723  * License: http://www.tinymce.com/license
29724  * Contributing: http://www.tinymce.com/contributing
29725  */
29726 
29727 /**
29728  * This layout manager works similar to the CSS flex box.
29729  *
29730  * @setting {String} direction row|row-reverse|column|column-reverse
29731  * @setting {Number} flex A positive-number to flex by.
29732  * @setting {String} align start|end|center|stretch
29733  * @setting {String} pack start|end|justify
29734  *
29735  * @class tinymce.ui.FlexLayout
29736  * @extends tinymce.ui.AbsoluteLayout
29737  */
29738 define("tinymce/ui/FlexLayout", [
29739 	"tinymce/ui/AbsoluteLayout"
29740 ], function(AbsoluteLayout) {
29741 	"use strict";
29742 
29743 	return AbsoluteLayout.extend({
29744 		/**
29745 		 * Recalculates the positions of the controls in the specified container.
29746 		 *
29747 		 * @method recalc
29748 		 * @param {tinymce.ui.Container} container Container instance to recalc.
29749 		 */
29750 		recalc: function(container) {
29751 			// A ton of variables, needs to be in the same scope for performance
29752 			var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
29753 			var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
29754 			var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName;
29755 			var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName;
29756 			var alignDeltaSizeName, alignContentSizeName;
29757 			var max = Math.max, min = Math.min;
29758 
29759 			// Get container items, properties and settings
29760 			items = container.items().filter(':visible');
29761 			contLayoutRect = container.layoutRect();
29762 			contPaddingBox = container._paddingBox;
29763 			contSettings = container.settings;
29764 			direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction;
29765 			align = contSettings.align;
29766 			pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack;
29767 			spacing = contSettings.spacing || 0;
29768 
29769 			if (direction == "row-reversed" || direction == "column-reverse") {
29770 				items = items.set(items.toArray().reverse());
29771 				direction = direction.split('-')[0];
29772 			}
29773 
29774 			// Setup axis variable name for row/column direction since the calculations is the same
29775 			if (direction == "column") {
29776 				posName = "y";
29777 				sizeName = "h";
29778 				minSizeName = "minH";
29779 				maxSizeName = "maxH";
29780 				innerSizeName = "innerH";
29781 				beforeName = 'top';
29782 				deltaSizeName = "deltaH";
29783 				contentSizeName = "contentH";
29784 
29785 				alignBeforeName = "left";
29786 				alignSizeName = "w";
29787 				alignAxisName = "x";
29788 				alignInnerSizeName = "innerW";
29789 				alignMinSizeName = "minW";
29790 				alignAfterName = "right";
29791 				alignDeltaSizeName = "deltaW";
29792 				alignContentSizeName = "contentW";
29793 			} else {
29794 				posName = "x";
29795 				sizeName = "w";
29796 				minSizeName = "minW";
29797 				maxSizeName = "maxW";
29798 				innerSizeName = "innerW";
29799 				beforeName = 'left';
29800 				deltaSizeName = "deltaW";
29801 				contentSizeName = "contentW";
29802 
29803 				alignBeforeName = "top";
29804 				alignSizeName = "h";
29805 				alignAxisName = "y";
29806 				alignInnerSizeName = "innerH";
29807 				alignMinSizeName = "minH";
29808 				alignAfterName = "bottom";
29809 				alignDeltaSizeName = "deltaH";
29810 				alignContentSizeName = "contentH";
29811 			}
29812 
29813 			// Figure out total flex, availableSpace and collect any max size elements
29814 			availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
29815 			maxAlignEndPos = totalFlex = 0;
29816 			for (i = 0, l = items.length; i < l; i++) {
29817 				ctrl = items[i];
29818 				ctrlLayoutRect = ctrl.layoutRect();
29819 				ctrlSettings = ctrl.settings;
29820 				flex = ctrlSettings.flex;
29821 				availableSpace -= (i < l - 1 ? spacing : 0);
29822 
29823 				if (flex > 0) {
29824 					totalFlex += flex;
29825 
29826 					// Flexed item has a max size then we need to check if we will hit that size
29827 					if (ctrlLayoutRect[maxSizeName]) {
29828 						maxSizeItems.push(ctrl);
29829 					}
29830 
29831 					ctrlLayoutRect.flex = flex;
29832 				}
29833 
29834 				availableSpace -= ctrlLayoutRect[minSizeName];
29835 
29836 				// Calculate the align end position to be used to check for overflow/underflow
29837 				size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
29838 				if (size > maxAlignEndPos) {
29839 					maxAlignEndPos = size;
29840 				}
29841 			}
29842 
29843 			// Calculate minW/minH
29844 			rect = {};
29845 			if (availableSpace < 0) {
29846 				rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
29847 			} else {
29848 				rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
29849 			}
29850 
29851 			rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
29852 
29853 			rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
29854 			rect[alignContentSizeName] = maxAlignEndPos;
29855 			rect.minW = min(rect.minW, contLayoutRect.maxW);
29856 			rect.minH = min(rect.minH, contLayoutRect.maxH);
29857 			rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
29858 			rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
29859 
29860 			// Resize container container if minSize was changed
29861 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
29862 				rect.w = rect.minW;
29863 				rect.h = rect.minH;
29864 
29865 				container.layoutRect(rect);
29866 				this.recalc(container);
29867 
29868 				// Forced recalc for example if items are hidden/shown
29869 				if (container._lastRect === null) {
29870 					var parentCtrl = container.parent();
29871 					if (parentCtrl) {
29872 						parentCtrl._lastRect = null;
29873 						parentCtrl.recalc();
29874 					}
29875 				}
29876 
29877 				return;
29878 			}
29879 
29880 			// Handle max size elements, check if they will become to wide with current options
29881 			ratio = availableSpace / totalFlex;
29882 			for (i = 0, l = maxSizeItems.length; i < l; i++) {
29883 				ctrl = maxSizeItems[i];
29884 				ctrlLayoutRect = ctrl.layoutRect();
29885 				maxSize = ctrlLayoutRect[maxSizeName];
29886 				size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
29887 
29888 				if (size > maxSize) {
29889 					availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
29890 					totalFlex -= ctrlLayoutRect.flex;
29891 					ctrlLayoutRect.flex = 0;
29892 					ctrlLayoutRect.maxFlexSize = maxSize;
29893 				} else {
29894 					ctrlLayoutRect.maxFlexSize = 0;
29895 				}
29896 			}
29897 
29898 			// Setup new ratio, target layout rect, start position
29899 			ratio = availableSpace / totalFlex;
29900 			pos = contPaddingBox[beforeName];
29901 			rect = {};
29902 
29903 			// Handle pack setting moves the start position to end, center
29904 			if (totalFlex === 0) {
29905 				if (pack == "end") {
29906 					pos = availableSpace + contPaddingBox[beforeName];
29907 				} else if (pack == "center") {
29908 					pos = Math.round(
29909 						(contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
29910 					) + contPaddingBox[beforeName];
29911 
29912 					if (pos < 0) {
29913 						pos = contPaddingBox[beforeName];
29914 					}
29915 				} else if (pack == "justify") {
29916 					pos = contPaddingBox[beforeName];
29917 					spacing = Math.floor(availableSpace / (items.length - 1));
29918 				}
29919 			}
29920 
29921 			// Default aligning (start) the other ones needs to be calculated while doing the layout
29922 			rect[alignAxisName] = contPaddingBox[alignBeforeName];
29923 
29924 			// Start laying out controls
29925 			for (i = 0, l = items.length; i < l; i++) {
29926 				ctrl = items[i];
29927 				ctrlLayoutRect = ctrl.layoutRect();
29928 				size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
29929 
29930 				// Align the control on the other axis
29931 				if (align === "center") {
29932 					rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
29933 				} else if (align === "stretch") {
29934 					rect[alignSizeName] = max(
29935 						ctrlLayoutRect[alignMinSizeName] || 0,
29936 						contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
29937 					);
29938 					rect[alignAxisName] = contPaddingBox[alignBeforeName];
29939 				} else if (align === "end") {
29940 					rect[alignAxisName] = contLayoutRect[alignInnerSizeName]  - ctrlLayoutRect[alignSizeName]  - contPaddingBox.top;
29941 				}
29942 
29943 				// Calculate new size based on flex
29944 				if (ctrlLayoutRect.flex > 0) {
29945 					size += ctrlLayoutRect.flex * ratio;
29946 				}
29947 
29948 				rect[sizeName] = size;
29949 				rect[posName] = pos;
29950 				ctrl.layoutRect(rect);
29951 
29952 				// Recalculate containers
29953 				if (ctrl.recalc) {
29954 					ctrl.recalc();
29955 				}
29956 
29957 				// Move x/y position
29958 				pos += size + spacing;
29959 			}
29960 		}
29961 	});
29962 });
29963 
29964 // Included from: js/tinymce/classes/ui/FlowLayout.js
29965 
29966 /**
29967  * FlowLayout.js
29968  *
29969  * Copyright, Moxiecode Systems AB
29970  * Released under LGPL License.
29971  *
29972  * License: http://www.tinymce.com/license
29973  * Contributing: http://www.tinymce.com/contributing
29974  */
29975 
29976 /**
29977  * This layout manager will place the controls by using the browsers native layout.
29978  *
29979  * @-x-less FlowLayout.less
29980  * @class tinymce.ui.FlowLayout
29981  * @extends tinymce.ui.Layout
29982  */
29983 define("tinymce/ui/FlowLayout", [
29984 	"tinymce/ui/Layout"
29985 ], function(Layout) {
29986 	return Layout.extend({
29987 		Defaults: {
29988 			containerClass: 'flow-layout',
29989 			controlClass: 'flow-layout-item',
29990 			endClass : 'break'
29991 		},
29992 
29993 		/**
29994 		 * Recalculates the positions of the controls in the specified container.
29995 		 *
29996 		 * @method recalc
29997 		 * @param {tinymce.ui.Container} container Container instance to recalc.
29998 		 */
29999 		recalc: function(container) {
30000 			container.items().filter(':visible').each(function(ctrl) {
30001 				if (ctrl.recalc) {
30002 					ctrl.recalc();
30003 				}
30004 			});
30005 		}
30006 	});
30007 });
30008 
30009 // Included from: js/tinymce/classes/ui/FormatControls.js
30010 
30011 /**
30012  * FormatControls.js
30013  *
30014  * Copyright, Moxiecode Systems AB
30015  * Released under LGPL License.
30016  *
30017  * License: http://www.tinymce.com/license
30018  * Contributing: http://www.tinymce.com/contributing
30019  */
30020 
30021 /**
30022  * Internal class containing all TinyMCE specific control types such as
30023  * format listboxes, fontlist boxes, toolbar buttons etc.
30024  *
30025  * @class tinymce.ui.FormatControls
30026  */
30027 define("tinymce/ui/FormatControls", [
30028 	"tinymce/ui/Control",
30029 	"tinymce/ui/Widget",
30030 	"tinymce/ui/FloatPanel",
30031 	"tinymce/util/Tools",
30032 	"tinymce/EditorManager",
30033 	"tinymce/Env"
30034 ], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
30035 	var each = Tools.each;
30036 
30037 	EditorManager.on('AddEditor', function(e) {
30038 		if (e.editor.rtl) {
30039 			Control.rtl = true;
30040 		}
30041 
30042 		registerControls(e.editor);
30043 	});
30044 
30045 	Control.translate = function(text) {
30046 		return EditorManager.translate(text);
30047 	};
30048 
30049 	Widget.tooltips = !Env.iOS;
30050 
30051 	function registerControls(editor) {
30052 		var formatMenu;
30053 
30054 		function createListBoxChangeHandler(items, formatName) {
30055 			return function() {
30056 				var self = this;
30057 
30058 				editor.on('nodeChange', function(e) {
30059 					var formatter = editor.formatter;
30060 					var value = null;
30061 
30062 					each(e.parents, function(node) {
30063 						each(items, function(item) {
30064 							if (formatName) {
30065 								if (formatter.matchNode(node, formatName, {value: item.value})) {
30066 									value = item.value;
30067 								}
30068 							} else {
30069 								if (formatter.matchNode(node, item.value)) {
30070 									value = item.value;
30071 								}
30072 							}
30073 
30074 							if (value) {
30075 								return false;
30076 							}
30077 						});
30078 
30079 						if (value) {
30080 							return false;
30081 						}
30082 					});
30083 
30084 					self.value(value);
30085 				});
30086 			};
30087 		}
30088 
30089 		function createFormats(formats) {
30090 			formats = formats.replace(/;$/, '').split(';');
30091 
30092 			var i = formats.length;
30093 			while (i--) {
30094 				formats[i] = formats[i].split('=');
30095 			}
30096 
30097 			return formats;
30098 		}
30099 
30100 		function createFormatMenu() {
30101 			var count = 0, newFormats = [];
30102 
30103 			var defaultStyleFormats = [
30104 				{title: 'Headings', items: [
30105 					{title: 'Heading 1', format: 'h1'},
30106 					{title: 'Heading 2', format: 'h2'},
30107 					{title: 'Heading 3', format: 'h3'},
30108 					{title: 'Heading 4', format: 'h4'},
30109 					{title: 'Heading 5', format: 'h5'},
30110 					{title: 'Heading 6', format: 'h6'}
30111 				]},
30112 
30113 				{title: 'Inline', items: [
30114 					{title: 'Bold', icon: 'bold', format: 'bold'},
30115 					{title: 'Italic', icon: 'italic', format: 'italic'},
30116 					{title: 'Underline', icon: 'underline', format: 'underline'},
30117 					{title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'},
30118 					{title: 'Superscript', icon: 'superscript', format: 'superscript'},
30119 					{title: 'Subscript', icon: 'subscript', format: 'subscript'},
30120 					{title: 'Code', icon: 'code', format: 'code'}
30121 				]},
30122 
30123 				{title: 'Blocks', items: [
30124 					{title: 'Paragraph', format: 'p'},
30125 					{title: 'Blockquote', format: 'blockquote'},
30126 					{title: 'Div', format: 'div'},
30127 					{title: 'Pre', format: 'pre'}
30128 				]},
30129 
30130 				{title: 'Alignment', items: [
30131 					{title: 'Left', icon: 'alignleft', format: 'alignleft'},
30132 					{title: 'Center', icon: 'aligncenter', format: 'aligncenter'},
30133 					{title: 'Right', icon: 'alignright', format: 'alignright'},
30134 					{title: 'Justify', icon: 'alignjustify', format: 'alignjustify'}
30135 				]}
30136 			];
30137 
30138 			function createMenu(formats) {
30139 				var menu = [];
30140 
30141 				if (!formats) {
30142 					return;
30143 				}
30144 
30145 				each(formats, function(format) {
30146 					var menuItem = {
30147 						text: format.title,
30148 						icon: format.icon
30149 					};
30150 
30151 					if (format.items) {
30152 						menuItem.menu = createMenu(format.items);
30153 					} else {
30154 						var formatName = format.format || "custom" + count++;
30155 
30156 						if (!format.format) {
30157 							format.name = formatName;
30158 							newFormats.push(format);
30159 						}
30160 
30161 						menuItem.format = formatName;
30162 					}
30163 
30164 					menu.push(menuItem);
30165 				});
30166 
30167 				return menu;
30168 			}
30169 
30170 			function createStylesMenu() {
30171 				var menu;
30172 
30173 				if (editor.settings.style_formats_merge) {
30174 					if (editor.settings.style_formats) {
30175 						menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats));
30176 					} else {
30177 						menu = createMenu(defaultStyleFormats);
30178 					}
30179 				} else {
30180 					menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
30181 				}
30182 
30183 				return menu;
30184 			}
30185 
30186 			editor.on('init', function() {
30187 				each(newFormats, function(format) {
30188 					editor.formatter.register(format.name, format);
30189 				});
30190 			});
30191 
30192 			return {
30193 				type: 'menu',
30194 				items: createStylesMenu(),
30195 				onPostRender: function(e) {
30196 					editor.fire('renderFormatsMenu', {control: e.control});
30197 				},
30198 				itemDefaults: {
30199 					preview: true,
30200 
30201 					textStyle: function() {
30202 						if (this.settings.format) {
30203 							return editor.formatter.getCssText(this.settings.format);
30204 						}
30205 					},
30206 
30207 					onPostRender: function() {
30208 						var self = this, formatName = this.settings.format;
30209 
30210 						if (formatName) {
30211 							self.parent().on('show', function() {
30212 								self.disabled(!editor.formatter.canApply(formatName));
30213 								self.active(editor.formatter.match(formatName));
30214 							});
30215 						}
30216 					},
30217 
30218 					onclick: function() {
30219 						if (this.settings.format) {
30220 							toggleFormat(this.settings.format);
30221 						}
30222 					}
30223 				}
30224 			};
30225 		}
30226 
30227 		formatMenu = createFormatMenu();
30228 
30229 		// Simple format controls <control/format>:<UI text>
30230 		each({
30231 			bold: 'Bold',
30232 			italic: 'Italic',
30233 			underline: 'Underline',
30234 			strikethrough: 'Strikethrough',
30235 			subscript: 'Subscript',
30236 			superscript: 'Superscript'
30237 		}, function(text, name) {
30238 			editor.addButton(name, {
30239 				tooltip: text,
30240 				onPostRender: function() {
30241 					var self = this;
30242 
30243 					// TODO: Fix this
30244 					if (editor.formatter) {
30245 						editor.formatter.formatChanged(name, function(state) {
30246 							self.active(state);
30247 						});
30248 					} else {
30249 						editor.on('init', function() {
30250 							editor.formatter.formatChanged(name, function(state) {
30251 								self.active(state);
30252 							});
30253 						});
30254 					}
30255 				},
30256 				onclick: function() {
30257 					toggleFormat(name);
30258 				}
30259 			});
30260 		});
30261 
30262 		// Simple command controls <control>:[<UI text>,<Command>]
30263 		each({
30264 			outdent: ['Decrease indent', 'Outdent'],
30265 			indent: ['Increase indent', 'Indent'],
30266 			cut: ['Cut', 'Cut'],
30267 			copy: ['Copy', 'Copy'],
30268 			paste: ['Paste', 'Paste'],
30269 			help: ['Help', 'mceHelp'],
30270 			selectall: ['Select all', 'SelectAll'],
30271 			hr: ['Insert horizontal rule', 'InsertHorizontalRule'],
30272 			removeformat: ['Clear formatting', 'RemoveFormat'],
30273 			visualaid: ['Visual aids', 'mceToggleVisualAid'],
30274 			newdocument: ['New document', 'mceNewDocument']
30275 		}, function(item, name) {
30276 			editor.addButton(name, {
30277 				tooltip: item[0],
30278 				cmd: item[1]
30279 			});
30280 		});
30281 
30282 		// Simple command controls with format state
30283 		each({
30284 			blockquote: ['Blockquote', 'mceBlockQuote'],
30285 			numlist: ['Numbered list', 'InsertOrderedList'],
30286 			bullist: ['Bullet list', 'InsertUnorderedList'],
30287 			subscript: ['Subscript', 'Subscript'],
30288 			superscript: ['Superscript', 'Superscript'],
30289 			alignleft: ['Align left', 'JustifyLeft'],
30290 			aligncenter: ['Align center', 'JustifyCenter'],
30291 			alignright: ['Align right', 'JustifyRight'],
30292 			alignjustify: ['Justify', 'JustifyFull']
30293 		}, function(item, name) {
30294 			editor.addButton(name, {
30295 				tooltip: item[0],
30296 				cmd: item[1],
30297 				onPostRender: function() {
30298 					var self = this;
30299 
30300 					// TODO: Fix this
30301 					if (editor.formatter) {
30302 						editor.formatter.formatChanged(name, function(state) {
30303 							self.active(state);
30304 						});
30305 					} else {
30306 						editor.on('init', function() {
30307 							editor.formatter.formatChanged(name, function(state) {
30308 								self.active(state);
30309 							});
30310 						});
30311 					}
30312 				}
30313 			});
30314 		});
30315 
30316 		function hasUndo() {
30317 			return editor.undoManager ? editor.undoManager.hasUndo() : false;
30318 		}
30319 
30320 		function hasRedo() {
30321 			return editor.undoManager ? editor.undoManager.hasRedo() : false;
30322 		}
30323 
30324 		function toggleUndoState() {
30325 			var self = this;
30326 
30327 			self.disabled(!hasUndo());
30328 			editor.on('Undo Redo AddUndo TypingUndo', function() {
30329 				self.disabled(!hasUndo());
30330 			});
30331 		}
30332 
30333 		function toggleRedoState() {
30334 			var self = this;
30335 
30336 			self.disabled(!hasRedo());
30337 			editor.on('Undo Redo AddUndo TypingUndo', function() {
30338 				self.disabled(!hasRedo());
30339 			});
30340 		}
30341 
30342 		function toggleVisualAidState() {
30343 			var self = this;
30344 
30345 			editor.on('VisualAid', function(e) {
30346 				self.active(e.hasVisual);
30347 			});
30348 
30349 			self.active(editor.hasVisual);
30350 		}
30351 
30352 		editor.addButton('undo', {
30353 			tooltip: 'Undo',
30354 			onPostRender: toggleUndoState,
30355 			cmd: 'undo'
30356 		});
30357 
30358 		editor.addButton('redo', {
30359 			tooltip: 'Redo',
30360 			onPostRender: toggleRedoState,
30361 			cmd: 'redo'
30362 		});
30363 
30364 		editor.addMenuItem('newdocument', {
30365 			text: 'New document',
30366 			shortcut: 'Ctrl+N',
30367 			icon: 'newdocument',
30368 			cmd: 'mceNewDocument'
30369 		});
30370 
30371 		editor.addMenuItem('undo', {
30372 			text: 'Undo',
30373 			icon: 'undo',
30374 			shortcut: 'Ctrl+Z',
30375 			onPostRender: toggleUndoState,
30376 			cmd: 'undo'
30377 		});
30378 
30379 		editor.addMenuItem('redo', {
30380 			text: 'Redo',
30381 			icon: 'redo',
30382 			shortcut: 'Ctrl+Y',
30383 			onPostRender: toggleRedoState,
30384 			cmd: 'redo'
30385 		});
30386 
30387 		editor.addMenuItem('visualaid', {
30388 			text: 'Visual aids',
30389 			selectable: true,
30390 			onPostRender: toggleVisualAidState,
30391 			cmd: 'mceToggleVisualAid'
30392 		});
30393 
30394 		each({
30395 			cut: ['Cut', 'Cut', 'Ctrl+X'],
30396 			copy: ['Copy', 'Copy', 'Ctrl+C'],
30397 			paste: ['Paste', 'Paste', 'Ctrl+V'],
30398 			selectall: ['Select all', 'SelectAll', 'Ctrl+A'],
30399 			bold: ['Bold', 'Bold', 'Ctrl+B'],
30400 			italic: ['Italic', 'Italic', 'Ctrl+I'],
30401 			underline: ['Underline', 'Underline'],
30402 			strikethrough: ['Strikethrough', 'Strikethrough'],
30403 			subscript: ['Subscript', 'Subscript'],
30404 			superscript: ['Superscript', 'Superscript'],
30405 			removeformat: ['Clear formatting', 'RemoveFormat']
30406 		}, function(item, name) {
30407 			editor.addMenuItem(name, {
30408 				text: item[0],
30409 				icon: name,
30410 				shortcut: item[2],
30411 				cmd: item[1]
30412 			});
30413 		});
30414 
30415 		editor.on('mousedown', function() {
30416 			FloatPanel.hideAll();
30417 		});
30418 
30419 		function toggleFormat(fmt) {
30420 			if (fmt.control) {
30421 				fmt = fmt.control.value();
30422 			}
30423 
30424 			if (fmt) {
30425 				editor.execCommand('mceToggleFormat', false, fmt);
30426 			}
30427 		}
30428 
30429 		editor.addButton('styleselect', {
30430 			type: 'menubutton',
30431 			text: 'Formats',
30432 			menu: formatMenu
30433 		});
30434 
30435 		editor.addButton('formatselect', function() {
30436 			var items = [], blocks = createFormats(editor.settings.block_formats ||
30437 				'Paragraph=p;' +
30438 				'Address=address;' +
30439 				'Pre=pre;' +
30440 				'Heading 1=h1;' +
30441 				'Heading 2=h2;' +
30442 				'Heading 3=h3;' +
30443 				'Heading 4=h4;' +
30444 				'Heading 5=h5;' +
30445 				'Heading 6=h6'
30446 			);
30447 
30448 			each(blocks, function(block) {
30449 				items.push({
30450 					text: block[0],
30451 					value: block[1],
30452 					textStyle: function() {
30453 						return editor.formatter.getCssText(block[1]);
30454 					}
30455 				});
30456 			});
30457 
30458 			return {
30459 				type: 'listbox',
30460 				text: blocks[0][0],
30461 				values: items,
30462 				fixedWidth: true,
30463 				onselect: toggleFormat,
30464 				onPostRender: createListBoxChangeHandler(items)
30465 			};
30466 		});
30467 
30468 		editor.addButton('fontselect', function() {
30469 			var defaultFontsFormats =
30470 				'Andale Mono=andale mono,times;' +
30471 				'Arial=arial,helvetica,sans-serif;' +
30472 				'Arial Black=arial black,avant garde;' +
30473 				'Book Antiqua=book antiqua,palatino;' +
30474 				'Comic Sans MS=comic sans ms,sans-serif;' +
30475 				'Courier New=courier new,courier;' +
30476 				'Georgia=georgia,palatino;' +
30477 				'Helvetica=helvetica;' +
30478 				'Impact=impact,chicago;' +
30479 				'Symbol=symbol;' +
30480 				'Tahoma=tahoma,arial,helvetica,sans-serif;' +
30481 				'Terminal=terminal,monaco;' +
30482 				'Times New Roman=times new roman,times;' +
30483 				'Trebuchet MS=trebuchet ms,geneva;' +
30484 				'Verdana=verdana,geneva;' +
30485 				'Webdings=webdings;' +
30486 				'Wingdings=wingdings,zapf dingbats';
30487 
30488 			var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
30489 
30490 			each(fonts, function(font) {
30491 				items.push({
30492 					text: {raw: font[0]},
30493 					value: font[1],
30494 					textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
30495 				});
30496 			});
30497 
30498 			return {
30499 				type: 'listbox',
30500 				text: 'Font Family',
30501 				tooltip: 'Font Family',
30502 				values: items,
30503 				fixedWidth: true,
30504 				onPostRender: createListBoxChangeHandler(items, 'fontname'),
30505 				onselect: function(e) {
30506 					if (e.control.settings.value) {
30507 						editor.execCommand('FontName', false, e.control.settings.value);
30508 					}
30509 				}
30510 			};
30511 		});
30512 
30513 		editor.addButton('fontsizeselect', function() {
30514 			var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
30515 			var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
30516 
30517 			each(fontsize_formats.split(' '), function(item) {
30518 				items.push({text: item, value: item});
30519 			});
30520 
30521 			return {
30522 				type: 'listbox',
30523 				text: 'Font Sizes',
30524 				tooltip: 'Font Sizes',
30525 				values: items,
30526 				fixedWidth: true,
30527 				onPostRender: createListBoxChangeHandler(items, 'fontsize'),
30528 				onclick: function(e) {
30529 					if (e.control.settings.value) {
30530 						editor.execCommand('FontSize', false, e.control.settings.value);
30531 					}
30532 				}
30533 			};
30534 		});
30535 
30536 		editor.addMenuItem('formats', {
30537 			text: 'Formats',
30538 			menu: formatMenu
30539 		});
30540 	}
30541 });
30542 
30543 // Included from: js/tinymce/classes/ui/GridLayout.js
30544 
30545 /**
30546  * GridLayout.js
30547  *
30548  * Copyright, Moxiecode Systems AB
30549  * Released under LGPL License.
30550  *
30551  * License: http://www.tinymce.com/license
30552  * Contributing: http://www.tinymce.com/contributing
30553  */
30554 
30555 /**
30556  * This layout manager places controls in a grid.
30557  *
30558  * @setting {Number} spacing Spacing between controls.
30559  * @setting {Number} spacingH Horizontal spacing between controls.
30560  * @setting {Number} spacingV Vertical spacing between controls.
30561  * @setting {Number} columns Number of columns to use.
30562  * @setting {String/Array} alignH start|end|center|stretch or array of values for each column.
30563  * @setting {String/Array} alignV start|end|center|stretch or array of values for each column.
30564  * @setting {String} pack start|end
30565  *
30566  * @class tinymce.ui.GridLayout
30567  * @extends tinymce.ui.AbsoluteLayout
30568  */
30569 define("tinymce/ui/GridLayout", [
30570 	"tinymce/ui/AbsoluteLayout"
30571 ], function(AbsoluteLayout) {
30572 	"use strict";
30573 
30574 	return AbsoluteLayout.extend({
30575 		/**
30576 		 * Recalculates the positions of the controls in the specified container.
30577 		 *
30578 		 * @method recalc
30579 		 * @param {tinymce.ui.Container} container Container instance to recalc.
30580 		 */
30581 		recalc: function(container) {
30582 			var settings = container.settings, rows, cols, items, contLayoutRect, width, height, rect,
30583 				ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY,
30584 				colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight;
30585 
30586 			// Get layout settings
30587 			settings = container.settings;
30588 			items = container.items().filter(':visible');
30589 			contLayoutRect = container.layoutRect();
30590 			cols = settings.columns || Math.ceil(Math.sqrt(items.length));
30591 			rows = Math.ceil(items.length / cols);
30592 			spacingH = settings.spacingH || settings.spacing || 0;
30593 			spacingV = settings.spacingV || settings.spacing || 0;
30594 			alignH = settings.alignH || settings.align;
30595 			alignV = settings.alignV || settings.align;
30596 			contPaddingBox = container._paddingBox;
30597 
30598 			if (alignH && typeof(alignH) == "string") {
30599 				alignH = [alignH];
30600 			}
30601 
30602 			if (alignV && typeof(alignV) == "string") {
30603 				alignV = [alignV];
30604 			}
30605 
30606 			// Zero padd columnWidths
30607 			for (x = 0; x < cols; x++) {
30608 				colWidths.push(0);
30609 			}
30610 
30611 			// Zero padd rowHeights
30612 			for (y = 0; y < rows; y++) {
30613 				rowHeights.push(0);
30614 			}
30615 
30616 			// Calculate columnWidths and rowHeights
30617 			for (y = 0; y < rows; y++) {
30618 				for (x = 0; x < cols; x++) {
30619 					ctrl = items[y * cols + x];
30620 
30621 					// Out of bounds
30622 					if (!ctrl) {
30623 						break;
30624 					}
30625 
30626 					ctrlLayoutRect = ctrl.layoutRect();
30627 					ctrlMinWidth = ctrlLayoutRect.minW;
30628 					ctrlMinHeight = ctrlLayoutRect.minH;
30629 
30630 					colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x];
30631 					rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y];
30632 				}
30633 			}
30634 
30635 			// Calculate maxX
30636 			availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right;
30637 			for (maxX = 0, x = 0; x < cols; x++) {
30638 				maxX += colWidths[x] + (x > 0 ? spacingH : 0);
30639 				availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x];
30640 			}
30641 
30642 			// Calculate maxY
30643 			availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom;
30644 			for (maxY = 0, y = 0; y < rows; y++) {
30645 				maxY += rowHeights[y] + (y > 0 ? spacingV : 0);
30646 				availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y];
30647 			}
30648 
30649 			maxX += contPaddingBox.left + contPaddingBox.right;
30650 			maxY += contPaddingBox.top + contPaddingBox.bottom;
30651 
30652 			// Calculate minW/minH
30653 			rect = {};
30654 			rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW);
30655 			rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH);
30656 
30657 			rect.contentW = rect.minW - contLayoutRect.deltaW;
30658 			rect.contentH = rect.minH - contLayoutRect.deltaH;
30659 			rect.minW = Math.min(rect.minW, contLayoutRect.maxW);
30660 			rect.minH = Math.min(rect.minH, contLayoutRect.maxH);
30661 			rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth);
30662 			rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight);
30663 
30664 			// Resize container container if minSize was changed
30665 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
30666 				rect.w = rect.minW;
30667 				rect.h = rect.minH;
30668 
30669 				container.layoutRect(rect);
30670 				this.recalc(container);
30671 
30672 				// Forced recalc for example if items are hidden/shown
30673 				if (container._lastRect === null) {
30674 					var parentCtrl = container.parent();
30675 					if (parentCtrl) {
30676 						parentCtrl._lastRect = null;
30677 						parentCtrl.recalc();
30678 					}
30679 				}
30680 
30681 				return;
30682 			}
30683 
30684 			// Update contentW/contentH so absEnd moves correctly
30685 			if (contLayoutRect.autoResize) {
30686 				rect = container.layoutRect(rect);
30687 				rect.contentW = rect.minW - contLayoutRect.deltaW;
30688 				rect.contentH = rect.minH - contLayoutRect.deltaH;
30689 			}
30690 
30691 			var flexV;
30692 
30693 			if (settings.packV == 'start') {
30694 				flexV = 0;
30695 			} else {
30696 				flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0;
30697 			}
30698 
30699 			// Calculate totalFlex
30700 			var totalFlex = 0;
30701 			var flexWidths = settings.flexWidths;
30702 			if (flexWidths) {
30703 				for (x = 0; x < flexWidths.length; x++) {
30704 					totalFlex += flexWidths[x];
30705 				}
30706 			} else {
30707 				totalFlex = cols;
30708 			}
30709 
30710 			// Calculate new column widths based on flex values
30711 			var ratio = availableWidth / totalFlex;
30712 			for (x = 0; x < cols; x++) {
30713 				colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio;
30714 			}
30715 
30716 			// Move/resize controls
30717 			posY = contPaddingBox.top;
30718 			for (y = 0; y < rows; y++) {
30719 				posX = contPaddingBox.left;
30720 				height = rowHeights[y] + flexV;
30721 
30722 				for (x = 0; x < cols; x++) {
30723 					ctrl = items[y * cols + x];
30724 
30725 					// No more controls to render then break
30726 					if (!ctrl) {
30727 						break;
30728 					}
30729 
30730 					// Get control settings and calculate x, y
30731 					ctrlSettings = ctrl.settings;
30732 					ctrlLayoutRect = ctrl.layoutRect();
30733 					width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth);
30734 					ctrlLayoutRect.x = posX;
30735 					ctrlLayoutRect.y = posY;
30736 
30737 					// Align control horizontal
30738 					align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null);
30739 					if (align == "center") {
30740 						ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2);
30741 					} else if (align == "right") {
30742 						ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w;
30743 					} else if (align == "stretch") {
30744 						ctrlLayoutRect.w = width;
30745 					}
30746 
30747 					// Align control vertical
30748 					align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null);
30749 					if (align == "center") {
30750 						ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2);
30751 					} else  if (align == "bottom") {
30752 						ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h;
30753 					} else if (align == "stretch") {
30754 						ctrlLayoutRect.h = height;
30755 					}
30756 
30757 					ctrl.layoutRect(ctrlLayoutRect);
30758 
30759 					posX += width + spacingH;
30760 
30761 					if (ctrl.recalc) {
30762 						ctrl.recalc();
30763 					}
30764 				}
30765 
30766 				posY += height + spacingV;
30767 			}
30768 		}
30769 	});
30770 });
30771 
30772 // Included from: js/tinymce/classes/ui/Iframe.js
30773 
30774 /**
30775  * Iframe.js
30776  *
30777  * Copyright, Moxiecode Systems AB
30778  * Released under LGPL License.
30779  *
30780  * License: http://www.tinymce.com/license
30781  * Contributing: http://www.tinymce.com/contributing
30782  */
30783 
30784 /*jshint scripturl:true */
30785 
30786 /**
30787  * This class creates an iframe.
30788  *
30789  * @setting {String} url Url to open in the iframe.
30790  *
30791  * @-x-less Iframe.less
30792  * @class tinymce.ui.Iframe
30793  * @extends tinymce.ui.Widget
30794  */
30795 define("tinymce/ui/Iframe", [
30796 	"tinymce/ui/Widget"
30797 ], function(Widget) {
30798 	"use strict";
30799 
30800 	return Widget.extend({
30801 		/**
30802 		 * Renders the control as a HTML string.
30803 		 *
30804 		 * @method renderHtml
30805 		 * @return {String} HTML representing the control.
30806 		 */
30807 		renderHtml: function() {
30808 			var self = this;
30809 
30810 			self.addClass('iframe');
30811 			self.canFocus = false;
30812 
30813 			/*eslint no-script-url:0 */
30814 			return (
30815 				'<iframe id="' + self._id + '" class="' + self.classes() + '" tabindex="-1" src="' +
30816 				(self.settings.url || "javascript:\'\'") + '" frameborder="0"></iframe>'
30817 			);
30818 		},
30819 
30820 		/**
30821 		 * Setter for the iframe source.
30822 		 *
30823 		 * @method src
30824 		 * @param {String} src Source URL for iframe.
30825 		 */
30826 		src: function(src) {
30827 			this.getEl().src = src;
30828 		},
30829 
30830 		/**
30831 		 * Inner HTML for the iframe.
30832 		 *
30833 		 * @method html
30834 		 * @param {String} html HTML string to set as HTML inside the iframe.
30835 		 * @param {function} callback Optional callback to execute when the iframe body is filled with contents.
30836 		 * @return {tinymce.ui.Iframe} Current iframe control.
30837 		 */
30838 		html: function(html, callback) {
30839 			var self = this, body = this.getEl().contentWindow.document.body;
30840 
30841 			// Wait for iframe to initialize IE 10 takes time
30842 			if (!body) {
30843 				setTimeout(function() {
30844 					self.html(html);
30845 				}, 0);
30846 			} else {
30847 				body.innerHTML = html;
30848 
30849 				if (callback) {
30850 					callback();
30851 				}
30852 			}
30853 
30854 			return this;
30855 		}
30856 	});
30857 });
30858 
30859 // Included from: js/tinymce/classes/ui/Label.js
30860 
30861 /**
30862  * Label.js
30863  *
30864  * Copyright, Moxiecode Systems AB
30865  * Released under LGPL License.
30866  *
30867  * License: http://www.tinymce.com/license
30868  * Contributing: http://www.tinymce.com/contributing
30869  */
30870 
30871 /**
30872  * This class creates a label element. A label is a simple text control
30873  * that can be bound to other controls.
30874  *
30875  * @-x-less Label.less
30876  * @class tinymce.ui.Label
30877  * @extends tinymce.ui.Widget
30878  */
30879 define("tinymce/ui/Label", [
30880 	"tinymce/ui/Widget",
30881 	"tinymce/ui/DomUtils"
30882 ], function(Widget, DomUtils) {
30883 	"use strict";
30884 
30885 	return Widget.extend({
30886 		/**
30887 		 * Constructs a instance with the specified settings.
30888 		 *
30889 		 * @constructor
30890 		 * @param {Object} settings Name/value object with settings.
30891 		 * @param {Boolean} multiline Multiline label.
30892 		 */
30893 		init: function(settings) {
30894 			var self = this;
30895 
30896 			self._super(settings);
30897 			self.addClass('widget');
30898 			self.addClass('label');
30899 			self.canFocus = false;
30900 
30901 			if (settings.multiline) {
30902 				self.addClass('autoscroll');
30903 			}
30904 
30905 			if (settings.strong) {
30906 				self.addClass('strong');
30907 			}
30908 		},
30909 
30910 		/**
30911 		 * Initializes the current controls layout rect.
30912 		 * This will be executed by the layout managers to determine the
30913 		 * default minWidth/minHeight etc.
30914 		 *
30915 		 * @method initLayoutRect
30916 		 * @return {Object} Layout rect instance.
30917 		 */
30918 		initLayoutRect: function() {
30919 			var self = this, layoutRect = self._super();
30920 
30921 			if (self.settings.multiline) {
30922 				var size = DomUtils.getSize(self.getEl());
30923 
30924 				// Check if the text fits within maxW if not then try word wrapping it
30925 				if (size.width > layoutRect.maxW) {
30926 					layoutRect.minW = layoutRect.maxW;
30927 					self.addClass('multiline');
30928 				}
30929 
30930 				self.getEl().style.width = layoutRect.minW + 'px';
30931 				layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height);
30932 			}
30933 
30934 			return layoutRect;
30935 		},
30936 
30937 		/**
30938 		 * Repaints the control after a layout operation.
30939 		 *
30940 		 * @method repaint
30941 		 */
30942 		repaint: function() {
30943 			var self = this;
30944 
30945 			if (!self.settings.multiline) {
30946 				self.getEl().style.lineHeight = self.layoutRect().h + 'px';
30947 			}
30948 
30949 			return self._super();
30950 		},
30951 
30952 		/**
30953 		 * Sets/gets the current label text.
30954 		 *
30955 		 * @method text
30956 		 * @param {String} [text] New label text.
30957 		 * @return {String|tinymce.ui.Label} Current text or current label instance.
30958 		 */
30959 		text: function(text) {
30960 			var self = this;
30961 
30962 			if (self._rendered && text) {
30963 				this.innerHtml(self.encode(text));
30964 			}
30965 
30966 			return self._super(text);
30967 		},
30968 
30969 		/**
30970 		 * Renders the control as a HTML string.
30971 		 *
30972 		 * @method renderHtml
30973 		 * @return {String} HTML representing the control.
30974 		 */
30975 		renderHtml: function() {
30976 			var self = this, forId = self.settings.forId;
30977 
30978 			return (
30979 				'<label id="' + self._id + '" class="' + self.classes() + '"' + (forId ? ' for="' + forId + '"' : '') + '>' +
30980 					self.encode(self._text) +
30981 				'</label>'
30982 			);
30983 		}
30984 	});
30985 });
30986 
30987 // Included from: js/tinymce/classes/ui/Toolbar.js
30988 
30989 /**
30990  * Toolbar.js
30991  *
30992  * Copyright, Moxiecode Systems AB
30993  * Released under LGPL License.
30994  *
30995  * License: http://www.tinymce.com/license
30996  * Contributing: http://www.tinymce.com/contributing
30997  */
30998 
30999 /**
31000  * Creates a new toolbar.
31001  *
31002  * @class tinymce.ui.Toolbar
31003  * @extends tinymce.ui.Container
31004  */
31005 define("tinymce/ui/Toolbar", [
31006 	"tinymce/ui/Container"
31007 ], function(Container) {
31008 	"use strict";
31009 
31010 	return Container.extend({
31011 		Defaults: {
31012 			role: 'toolbar',
31013 			layout: 'flow'
31014 		},
31015 
31016 		/**
31017 		 * Constructs a instance with the specified settings.
31018 		 *
31019 		 * @constructor
31020 		 * @param {Object} settings Name/value object with settings.
31021 		 */
31022 		init: function(settings) {
31023 			var self = this;
31024 
31025 			self._super(settings);
31026 			self.addClass('toolbar');
31027 		},
31028 
31029 		/**
31030 		 * Called after the control has been rendered.
31031 		 *
31032 		 * @method postRender
31033 		 */
31034 		postRender: function() {
31035 			var self = this;
31036 
31037 			self.items().addClass('toolbar-item');
31038 
31039 			return self._super();
31040 		}
31041 	});
31042 });
31043 
31044 // Included from: js/tinymce/classes/ui/MenuBar.js
31045 
31046 /**
31047  * MenuBar.js
31048  *
31049  * Copyright, Moxiecode Systems AB
31050  * Released under LGPL License.
31051  *
31052  * License: http://www.tinymce.com/license
31053  * Contributing: http://www.tinymce.com/contributing
31054  */
31055 
31056 /**
31057  * Creates a new menubar.
31058  *
31059  * @-x-less MenuBar.less
31060  * @class tinymce.ui.MenuBar
31061  * @extends tinymce.ui.Container
31062  */
31063 define("tinymce/ui/MenuBar", [
31064 	"tinymce/ui/Toolbar"
31065 ], function(Toolbar) {
31066 	"use strict";
31067 
31068 	return Toolbar.extend({
31069 		Defaults: {
31070 			role: 'menubar',
31071 			containerCls: 'menubar',
31072 			ariaRoot: true,
31073 			defaults: {
31074 				type: 'menubutton'
31075 			}
31076 		}
31077 	});
31078 });
31079 
31080 // Included from: js/tinymce/classes/ui/MenuButton.js
31081 
31082 /**
31083  * MenuButton.js
31084  *
31085  * Copyright, Moxiecode Systems AB
31086  * Released under LGPL License.
31087  *
31088  * License: http://www.tinymce.com/license
31089  * Contributing: http://www.tinymce.com/contributing
31090  */
31091 
31092 /**
31093  * Creates a new menu button.
31094  *
31095  * @-x-less MenuButton.less
31096  * @class tinymce.ui.MenuButton
31097  * @extends tinymce.ui.Button
31098  */
31099 define("tinymce/ui/MenuButton", [
31100 	"tinymce/ui/Button",
31101 	"tinymce/ui/Factory",
31102 	"tinymce/ui/MenuBar"
31103 ], function(Button, Factory, MenuBar) {
31104 	"use strict";
31105 
31106 	// TODO: Maybe add as some global function
31107 	function isChildOf(node, parent) {
31108 		while (node) {
31109 			if (parent === node) {
31110 				return true;
31111 			}
31112 
31113 			node = node.parentNode;
31114 		}
31115 
31116 		return false;
31117 	}
31118 
31119 	var MenuButton = Button.extend({
31120 		/**
31121 		 * Constructs a instance with the specified settings.
31122 		 *
31123 		 * @constructor
31124 		 * @param {Object} settings Name/value object with settings.
31125 		 */
31126 		init: function(settings) {
31127 			var self = this;
31128 
31129 			self._renderOpen = true;
31130 			self._super(settings);
31131 
31132 			self.addClass('menubtn');
31133 
31134 			if (settings.fixedWidth) {
31135 				self.addClass('fixed-width');
31136 			}
31137 
31138 			self.aria('haspopup', true);
31139 			self.hasPopup = true;
31140 		},
31141 
31142 		/**
31143 		 * Shows the menu for the button.
31144 		 *
31145 		 * @method showMenu
31146 		 */
31147 		showMenu: function() {
31148 			var self = this, settings = self.settings, menu;
31149 
31150 			if (self.menu && self.menu.visible()) {
31151 				return self.hideMenu();
31152 			}
31153 
31154 			if (!self.menu) {
31155 				menu = settings.menu || [];
31156 
31157 				// Is menu array then auto constuct menu control
31158 				if (menu.length) {
31159 					menu = {
31160 						type: 'menu',
31161 						items: menu
31162 					};
31163 				} else {
31164 					menu.type = menu.type || 'menu';
31165 				}
31166 
31167 				self.menu = Factory.create(menu).parent(self).renderTo();
31168 				self.fire('createmenu');
31169 				self.menu.reflow();
31170 				self.menu.on('cancel', function(e) {
31171 					if (e.control.parent() === self.menu) {
31172 						e.stopPropagation();
31173 						self.focus();
31174 						self.hideMenu();
31175 					}
31176 				});
31177 
31178 				// Move focus to button when a menu item is selected/clicked
31179 				self.menu.on('select', function() {
31180 					self.focus();
31181 				});
31182 
31183 				self.menu.on('show hide', function(e) {
31184 					if (e.control == self.menu) {
31185 						self.activeMenu(e.type == 'show');
31186 					}
31187 
31188 					self.aria('expanded', e.type == 'show');
31189 				}).fire('show');
31190 			}
31191 
31192 			self.menu.show();
31193 			self.menu.layoutRect({w: self.layoutRect().w});
31194 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
31195 		},
31196 
31197 		/**
31198 		 * Hides the menu for the button.
31199 		 *
31200 		 * @method hideMenu
31201 		 */
31202 		hideMenu: function() {
31203 			var self = this;
31204 
31205 			if (self.menu) {
31206 				self.menu.items().each(function(item) {
31207 					if (item.hideMenu) {
31208 						item.hideMenu();
31209 					}
31210 				});
31211 
31212 				self.menu.hide();
31213 			}
31214 		},
31215 
31216 		/**
31217 		 * Sets the active menu state.
31218 		 *
31219 		 * @private
31220 		 */
31221 		activeMenu: function(state) {
31222 			this.toggleClass('active', state);
31223 		},
31224 
31225 		/**
31226 		 * Renders the control as a HTML string.
31227 		 *
31228 		 * @method renderHtml
31229 		 * @return {String} HTML representing the control.
31230 		 */
31231 		renderHtml: function() {
31232 			var self = this, id = self._id, prefix = self.classPrefix;
31233 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
31234 
31235 			self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button');
31236 
31237 			return (
31238 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
31239 					'<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' +
31240 						(icon ? '<i class="' + icon + '"></i>' : '') +
31241 						'<span>' + (self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') + '</span>' +
31242 						' <i class="' + prefix + 'caret"></i>' +
31243 					'</button>' +
31244 				'</div>'
31245 			);
31246 		},
31247 
31248 		/**
31249 		 * Gets invoked after the control has been rendered.
31250 		 *
31251 		 * @method postRender
31252 		 */
31253 		postRender: function() {
31254 			var self = this;
31255 
31256 			self.on('click', function(e) {
31257 				if (e.control === self && isChildOf(e.target, self.getEl())) {
31258 					self.showMenu();
31259 
31260 					if (e.aria) {
31261 						self.menu.items()[0].focus();
31262 					}
31263 				}
31264 			});
31265 
31266 			self.on('mouseenter', function(e) {
31267 				var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu;
31268 
31269 				if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) {
31270 					parent.items().filter('MenuButton').each(function(ctrl) {
31271 						if (ctrl.hideMenu && ctrl != overCtrl) {
31272 							if (ctrl.menu && ctrl.menu.visible()) {
31273 								hasVisibleSiblingMenu = true;
31274 							}
31275 
31276 							ctrl.hideMenu();
31277 						}
31278 					});
31279 
31280 					if (hasVisibleSiblingMenu) {
31281 						overCtrl.focus(); // Fix for: #5887
31282 						overCtrl.showMenu();
31283 					}
31284 				}
31285 			});
31286 
31287 			return self._super();
31288 		},
31289 
31290 		/**
31291 		 * Sets/gets the current button text.
31292 		 *
31293 		 * @method text
31294 		 * @param {String} [text] New button text.
31295 		 * @return {String|tinymce.ui.MenuButton} Current text or current MenuButton instance.
31296 		 */
31297 		text: function(text) {
31298 			var self = this, i, children;
31299 
31300 			if (self._rendered) {
31301 				children = self.getEl('open').getElementsByTagName('span');
31302 				for (i = 0; i < children.length; i++) {
31303 					children[i].innerHTML = (self.settings.icon && text ? '\u00a0' : '') + self.encode(text);
31304 				}
31305 			}
31306 
31307 			return this._super(text);
31308 		},
31309 
31310 		/**
31311 		 * Removes the control and it's menus.
31312 		 *
31313 		 * @method remove
31314 		 */
31315 		remove: function() {
31316 			this._super();
31317 
31318 			if (this.menu) {
31319 				this.menu.remove();
31320 			}
31321 		}
31322 	});
31323 
31324 	return MenuButton;
31325 });
31326 
31327 // Included from: js/tinymce/classes/ui/ListBox.js
31328 
31329 /**
31330  * ListBox.js
31331  *
31332  * Copyright, Moxiecode Systems AB
31333  * Released under LGPL License.
31334  *
31335  * License: http://www.tinymce.com/license
31336  * Contributing: http://www.tinymce.com/contributing
31337  */
31338 
31339 /**
31340  * Creates a new list box control.
31341  *
31342  * @-x-less ListBox.less
31343  * @class tinymce.ui.ListBox
31344  * @extends tinymce.ui.MenuButton
31345  */
31346 define("tinymce/ui/ListBox", [
31347 	"tinymce/ui/MenuButton"
31348 ], function(MenuButton) {
31349 	"use strict";
31350 
31351 	return MenuButton.extend({
31352 		/**
31353 		 * Constructs a instance with the specified settings.
31354 		 *
31355 		 * @constructor
31356 		 * @param {Object} settings Name/value object with settings.
31357 		 * @setting {Array} values Array with values to add to list box.
31358 		 */
31359 		init: function(settings) {
31360 			var self = this, values, i, selected, selectedText, lastItemCtrl;
31361 
31362 			self._values = values = settings.values;
31363 			if (values) {
31364 				for (i = 0; i < values.length; i++) {
31365 					selected = values[i].selected || settings.value === values[i].value;
31366 
31367 					if (selected) {
31368 						selectedText = selectedText || values[i].text;
31369 						self._value = values[i].value;
31370 						break;
31371 					}
31372 				}
31373 
31374 				// Default with first item
31375 				if (!selected && values.length > 0) {
31376 					selectedText = values[0].text;
31377 					self._value = values[0].value;
31378 				}
31379 
31380 				settings.menu = values;
31381 			}
31382 
31383 			settings.text = settings.text || selectedText || values[0].text;
31384 
31385 			self._super(settings);
31386 			self.addClass('listbox');
31387 
31388 			self.on('select', function(e) {
31389 				var ctrl = e.control;
31390 
31391 				if (lastItemCtrl) {
31392 					e.lastControl = lastItemCtrl;
31393 				}
31394 
31395 				if (settings.multiple) {
31396 					ctrl.active(!ctrl.active());
31397 				} else {
31398 					self.value(e.control.settings.value);
31399 				}
31400 
31401 				lastItemCtrl = ctrl;
31402 			});
31403 		},
31404 
31405 		/**
31406 		 * Getter/setter function for the control value.
31407 		 *
31408 		 * @method value
31409 		 * @param {String} [value] Value to be set.
31410 		 * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
31411 		 */
31412 		value: function(value) {
31413 			var self = this, active, selectedText, menu, i;
31414 
31415 			function activateByValue(menu, value) {
31416 				menu.items().each(function(ctrl) {
31417 					active = ctrl.value() === value;
31418 
31419 					if (active) {
31420 						selectedText = selectedText || ctrl.text();
31421 					}
31422 
31423 					ctrl.active(active);
31424 
31425 					if (ctrl.menu) {
31426 						activateByValue(ctrl.menu, value);
31427 					}
31428 				});
31429 			}
31430 
31431 			if (typeof(value) != "undefined") {
31432 				if (self.menu) {
31433 					activateByValue(self.menu, value);
31434 				} else {
31435 					menu = self.settings.menu;
31436 					for (i = 0; i < menu.length; i++) {
31437 						active = menu[i].value == value;
31438 
31439 						if (active) {
31440 							selectedText = selectedText || menu[i].text;
31441 						}
31442 
31443 						menu[i].active = active;
31444 					}
31445 				}
31446 
31447 				self.text(selectedText || this.settings.text);
31448 			}
31449 
31450 			return self._super(value);
31451 		}
31452 	});
31453 });
31454 
31455 // Included from: js/tinymce/classes/ui/MenuItem.js
31456 
31457 /**
31458  * MenuItem.js
31459  *
31460  * Copyright, Moxiecode Systems AB
31461  * Released under LGPL License.
31462  *
31463  * License: http://www.tinymce.com/license
31464  * Contributing: http://www.tinymce.com/contributing
31465  */
31466 
31467 /**
31468  * Creates a new menu item.
31469  *
31470  * @-x-less MenuItem.less
31471  * @class tinymce.ui.MenuItem
31472  * @extends tinymce.ui.Control
31473  */
31474 define("tinymce/ui/MenuItem", [
31475 	"tinymce/ui/Widget",
31476 	"tinymce/ui/Factory",
31477 	"tinymce/Env"
31478 ], function(Widget, Factory, Env) {
31479 	"use strict";
31480 
31481 	return Widget.extend({
31482 		Defaults: {
31483 			border: 0,
31484 			role: 'menuitem'
31485 		},
31486 
31487 		/**
31488 		 * Constructs a instance with the specified settings.
31489 		 *
31490 		 * @constructor
31491 		 * @param {Object} settings Name/value object with settings.
31492 		 * @setting {Boolean} selectable Selectable menu.
31493 		 * @setting {Array} menu Submenu array with items.
31494 		 * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X
31495 		 */
31496 		init: function(settings) {
31497 			var self = this;
31498 
31499 			self.hasPopup = true;
31500 
31501 			self._super(settings);
31502 
31503 			settings = self.settings;
31504 
31505 			self.addClass('menu-item');
31506 
31507 			if (settings.menu) {
31508 				self.addClass('menu-item-expand');
31509 			}
31510 
31511 			if (settings.preview) {
31512 				self.addClass('menu-item-preview');
31513 			}
31514 
31515 			if (self._text === '-' || self._text === '|') {
31516 				self.addClass('menu-item-sep');
31517 				self.aria('role', 'separator');
31518 				self._text = '-';
31519 			}
31520 
31521 			if (settings.selectable) {
31522 				self.aria('role', 'menuitemcheckbox');
31523 				self.addClass('menu-item-checkbox');
31524 				settings.icon = 'selected';
31525 			}
31526 
31527 			if (!settings.preview && !settings.selectable) {
31528 				self.addClass('menu-item-normal');
31529 			}
31530 
31531 			self.on('mousedown', function(e) {
31532 				e.preventDefault();
31533 			});
31534 
31535 			if (settings.menu && !settings.ariaHideMenu) {
31536 				self.aria('haspopup', true);
31537 			}
31538 		},
31539 
31540 		/**
31541 		 * Returns true/false if the menuitem has sub menu.
31542 		 *
31543 		 * @method hasMenus
31544 		 * @return {Boolean} True/false state if it has submenu.
31545 		 */
31546 		hasMenus: function() {
31547 			return !!this.settings.menu;
31548 		},
31549 
31550 		/**
31551 		 * Shows the menu for the menu item.
31552 		 *
31553 		 * @method showMenu
31554 		 */
31555 		showMenu: function() {
31556 			var self = this, settings = self.settings, menu, parent = self.parent();
31557 
31558 			parent.items().each(function(ctrl) {
31559 				if (ctrl !== self) {
31560 					ctrl.hideMenu();
31561 				}
31562 			});
31563 
31564 			if (settings.menu) {
31565 				menu = self.menu;
31566 
31567 				if (!menu) {
31568 					menu = settings.menu;
31569 
31570 					// Is menu array then auto constuct menu control
31571 					if (menu.length) {
31572 						menu = {
31573 							type: 'menu',
31574 							items: menu
31575 						};
31576 					} else {
31577 						menu.type = menu.type || 'menu';
31578 					}
31579 
31580 					if (parent.settings.itemDefaults) {
31581 						menu.itemDefaults = parent.settings.itemDefaults;
31582 					}
31583 
31584 					menu = self.menu = Factory.create(menu).parent(self).renderTo();
31585 					menu.reflow();
31586 					menu.fire('show');
31587 					menu.on('cancel', function(e) {
31588 						e.stopPropagation();
31589 						self.focus();
31590 						menu.hide();
31591 					});
31592 
31593 					menu.on('hide', function(e) {
31594 						if (e.control === menu) {
31595 							self.removeClass('selected');
31596 						}
31597 					});
31598 
31599 					menu.submenu = true;
31600 				} else {
31601 					menu.show();
31602 				}
31603 
31604 				menu._parentMenu = parent;
31605 
31606 				menu.addClass('menu-sub');
31607 
31608 				var rel = menu.testMoveRel(
31609 					self.getEl(),
31610 					self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br']
31611 				);
31612 
31613 				menu.moveRel(self.getEl(), rel);
31614 				menu.rel = rel;
31615 
31616 				rel = 'menu-sub-' + rel;
31617 				menu.removeClass(menu._lastRel);
31618 				menu.addClass(rel);
31619 				menu._lastRel = rel;
31620 
31621 				self.addClass('selected');
31622 				self.aria('expanded', true);
31623 			}
31624 		},
31625 
31626 		/**
31627 		 * Hides the menu for the menu item.
31628 		 *
31629 		 * @method hideMenu
31630 		 */
31631 		hideMenu: function() {
31632 			var self = this;
31633 
31634 			if (self.menu) {
31635 				self.menu.items().each(function(item) {
31636 					if (item.hideMenu) {
31637 						item.hideMenu();
31638 					}
31639 				});
31640 
31641 				self.menu.hide();
31642 				self.aria('expanded', false);
31643 			}
31644 
31645 			return self;
31646 		},
31647 
31648 		/**
31649 		 * Renders the control as a HTML string.
31650 		 *
31651 		 * @method renderHtml
31652 		 * @return {String} HTML representing the control.
31653 		 */
31654 		renderHtml: function() {
31655 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
31656 			var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
31657 
31658 			if (icon) {
31659 				self.parent().addClass('menu-has-icons');
31660 			}
31661 
31662 			if (settings.image) {
31663 				icon = 'none';
31664 				image = ' style="background-image: url(\'' + settings.image + '\')"';
31665 			}
31666 
31667 			if (shortcut && Env.mac) {
31668 				// format shortcut for Mac
31669 				shortcut = shortcut.replace(/ctrl\+alt\+/i, '⌥⌘'); // ctrl+cmd
31670 				shortcut = shortcut.replace(/ctrl\+/i, '⌘'); // ctrl symbol
31671 				shortcut = shortcut.replace(/alt\+/i, '⌥'); // cmd symbol
31672 				shortcut = shortcut.replace(/shift\+/i, '⇧'); // shift symbol
31673 			}
31674 
31675 			icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
31676 
31677 			return (
31678 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
31679 					(text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '') +
31680 					(text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
31681 					(shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') +
31682 					(settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
31683 				'</div>'
31684 			);
31685 		},
31686 
31687 		/**
31688 		 * Gets invoked after the control has been rendered.
31689 		 *
31690 		 * @method postRender
31691 		 */
31692 		postRender: function() {
31693 			var self = this, settings = self.settings;
31694 
31695 			var textStyle = settings.textStyle;
31696 			if (typeof(textStyle) == "function") {
31697 				textStyle = textStyle.call(this);
31698 			}
31699 
31700 			if (textStyle) {
31701 				var textElm = self.getEl('text');
31702 				if (textElm) {
31703 					textElm.setAttribute('style', textStyle);
31704 				}
31705 			}
31706 
31707 			self.on('mouseenter click', function(e) {
31708 				if (e.control === self) {
31709 					if (!settings.menu && e.type === 'click') {
31710 						self.fire('select');
31711 						self.parent().hideAll();
31712 					} else {
31713 						self.showMenu();
31714 
31715 						if (e.aria) {
31716 							self.menu.focus(true);
31717 						}
31718 					}
31719 				}
31720 			});
31721 
31722 			self._super();
31723 
31724 			return self;
31725 		},
31726 
31727 		active: function(state) {
31728 			if (typeof(state) != "undefined") {
31729 				this.aria('checked', state);
31730 			}
31731 
31732 			return this._super(state);
31733 		},
31734 
31735 		/**
31736 		 * Removes the control and it's menus.
31737 		 *
31738 		 * @method remove
31739 		 */
31740 		remove: function() {
31741 			this._super();
31742 
31743 			if (this.menu) {
31744 				this.menu.remove();
31745 			}
31746 		}
31747 	});
31748 });
31749 
31750 // Included from: js/tinymce/classes/ui/Menu.js
31751 
31752 /**
31753  * Menu.js
31754  *
31755  * Copyright, Moxiecode Systems AB
31756  * Released under LGPL License.
31757  *
31758  * License: http://www.tinymce.com/license
31759  * Contributing: http://www.tinymce.com/contributing
31760  */
31761 
31762 /**
31763  * Creates a new menu.
31764  *
31765  * @-x-less Menu.less
31766  * @class tinymce.ui.Menu
31767  * @extends tinymce.ui.FloatPanel
31768  */
31769 define("tinymce/ui/Menu", [
31770 	"tinymce/ui/FloatPanel",
31771 	"tinymce/ui/MenuItem",
31772 	"tinymce/util/Tools"
31773 ], function(FloatPanel, MenuItem, Tools) {
31774 	"use strict";
31775 
31776 	var Menu = FloatPanel.extend({
31777 		Defaults: {
31778 			defaultType: 'menuitem',
31779 			border: 1,
31780 			layout: 'stack',
31781 			role: 'application',
31782 			bodyRole: 'menu',
31783 			ariaRoot: true
31784 		},
31785 
31786 		/**
31787 		 * Constructs a instance with the specified settings.
31788 		 *
31789 		 * @constructor
31790 		 * @param {Object} settings Name/value object with settings.
31791 		 */
31792 		init: function(settings) {
31793 			var self = this;
31794 
31795 			settings.autohide = true;
31796 			settings.constrainToViewport = true;
31797 
31798 			if (settings.itemDefaults) {
31799 				var items = settings.items, i = items.length;
31800 
31801 				while (i--) {
31802 					items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
31803 				}
31804 			}
31805 
31806 			self._super(settings);
31807 			self.addClass('menu');
31808 		},
31809 
31810 		/**
31811 		 * Repaints the control after a layout operation.
31812 		 *
31813 		 * @method repaint
31814 		 */
31815 		repaint: function() {
31816 			this.toggleClass('menu-align', true);
31817 
31818 			this._super();
31819 
31820 			this.getEl().style.height = '';
31821 			this.getEl('body').style.height = '';
31822 
31823 			return this;
31824 		},
31825 
31826 		/**
31827 		 * Hides/closes the menu.
31828 		 *
31829 		 * @method cancel
31830 		 */
31831 		cancel: function() {
31832 			var self = this;
31833 
31834 			self.hideAll();
31835 			self.fire('select');
31836 		},
31837 
31838 		/**
31839 		 * Hide menu and all sub menus.
31840 		 *
31841 		 * @method hideAll
31842 		 */
31843 		hideAll: function() {
31844 			var self = this;
31845 
31846 			this.find('menuitem').exec('hideMenu');
31847 
31848 			return self._super();
31849 		},
31850 /*
31851 		getContainerElm: function() {
31852 			var doc = document, id = this.classPrefix + 'menucontainer';
31853 
31854 			var elm = doc.getElementById(id);
31855 			if (!elm) {
31856 				elm = doc.createElement('div');
31857 				elm.id = id;
31858 				elm.setAttribute('role', 'application');
31859 				elm.className = this.classPrefix + '-reset';
31860 				elm.style.position = 'absolute';
31861 				elm.style.top = elm.style.left = '0';
31862 				elm.style.overflow = 'visible';
31863 				doc.body.appendChild(elm);
31864 			}
31865 
31866 			return elm;
31867 		},
31868 */
31869 		/**
31870 		 * Invoked before the menu is rendered.
31871 		 *
31872 		 * @method preRender
31873 		 */
31874 		preRender: function() {
31875 			var self = this;
31876 
31877 			self.items().each(function(ctrl) {
31878 				var settings = ctrl.settings;
31879 
31880 				if (settings.icon || settings.selectable) {
31881 					self._hasIcons = true;
31882 					return false;
31883 				}
31884 			});
31885 
31886 			return self._super();
31887 		}
31888 	});
31889 
31890 	return Menu;
31891 });
31892 
31893 // Included from: js/tinymce/classes/ui/Radio.js
31894 
31895 /**
31896  * Radio.js
31897  *
31898  * Copyright, Moxiecode Systems AB
31899  * Released under LGPL License.
31900  *
31901  * License: http://www.tinymce.com/license
31902  * Contributing: http://www.tinymce.com/contributing
31903  */
31904 
31905 /**
31906  * Creates a new radio button.
31907  *
31908  * @-x-less Radio.less
31909  * @class tinymce.ui.Radio
31910  * @extends tinymce.ui.Checkbox
31911  */
31912 define("tinymce/ui/Radio", [
31913 	"tinymce/ui/Checkbox"
31914 ], function(Checkbox) {
31915 	"use strict";
31916 
31917 	return Checkbox.extend({
31918 		Defaults: {
31919 			classes: "radio",
31920 			role: "radio"
31921 		}
31922 	});
31923 });
31924 
31925 // Included from: js/tinymce/classes/ui/ResizeHandle.js
31926 
31927 /**
31928  * ResizeHandle.js
31929  *
31930  * Copyright, Moxiecode Systems AB
31931  * Released under LGPL License.
31932  *
31933  * License: http://www.tinymce.com/license
31934  * Contributing: http://www.tinymce.com/contributing
31935  */
31936 
31937 /**
31938  * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events.
31939  *
31940  * @-x-less ResizeHandle.less
31941  * @class tinymce.ui.ResizeHandle
31942  * @extends tinymce.ui.Widget
31943  */
31944 define("tinymce/ui/ResizeHandle", [
31945 	"tinymce/ui/Widget",
31946 	"tinymce/ui/DragHelper"
31947 ], function(Widget, DragHelper) {
31948 	"use strict";
31949 
31950 	return Widget.extend({
31951 		/**
31952 		 * Renders the control as a HTML string.
31953 		 *
31954 		 * @method renderHtml
31955 		 * @return {String} HTML representing the control.
31956 		 */
31957 		renderHtml: function() {
31958 			var self = this, prefix = self.classPrefix;
31959 
31960 			self.addClass('resizehandle');
31961 
31962 			if (self.settings.direction == "both") {
31963 				self.addClass('resizehandle-both');
31964 			}
31965 
31966 			self.canFocus = false;
31967 
31968 			return (
31969 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
31970 					'<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' +
31971 				'</div>'
31972 			);
31973 		},
31974 
31975 		/**
31976 		 * Called after the control has been rendered.
31977 		 *
31978 		 * @method postRender
31979 		 */
31980 		postRender: function() {
31981 			var self = this;
31982 
31983 			self._super();
31984 
31985 			self.resizeDragHelper = new DragHelper(this._id, {
31986 				start: function() {
31987 					self.fire('ResizeStart');
31988 				},
31989 
31990 				drag: function(e) {
31991 					if (self.settings.direction != "both") {
31992 						e.deltaX = 0;
31993 					}
31994 
31995 					self.fire('Resize', e);
31996 				},
31997 
31998 				stop: function() {
31999 					self.fire('ResizeEnd');
32000 				}
32001 			});
32002 		},
32003 
32004 		remove: function() {
32005 			if (this.resizeDragHelper) {
32006 				this.resizeDragHelper.destroy();
32007 			}
32008 
32009 			return this._super();
32010 		}
32011 	});
32012 });
32013 
32014 // Included from: js/tinymce/classes/ui/Spacer.js
32015 
32016 /**
32017  * Spacer.js
32018  *
32019  * Copyright, Moxiecode Systems AB
32020  * Released under LGPL License.
32021  *
32022  * License: http://www.tinymce.com/license
32023  * Contributing: http://www.tinymce.com/contributing
32024  */
32025 
32026 /**
32027  * Creates a spacer. This control is used in flex layouts for example.
32028  *
32029  * @-x-less Spacer.less
32030  * @class tinymce.ui.Spacer
32031  * @extends tinymce.ui.Widget
32032  */
32033 define("tinymce/ui/Spacer", [
32034 	"tinymce/ui/Widget"
32035 ], function(Widget) {
32036 	"use strict";
32037 
32038 	return Widget.extend({
32039 		/**
32040 		 * Renders the control as a HTML string.
32041 		 *
32042 		 * @method renderHtml
32043 		 * @return {String} HTML representing the control.
32044 		 */
32045 		renderHtml: function() {
32046 			var self = this;
32047 
32048 			self.addClass('spacer');
32049 			self.canFocus = false;
32050 
32051 			return '<div id="' + self._id + '" class="' + self.classes() + '"></div>';
32052 		}
32053 	});
32054 });
32055 
32056 // Included from: js/tinymce/classes/ui/SplitButton.js
32057 
32058 /**
32059  * SplitButton.js
32060  *
32061  * Copyright, Moxiecode Systems AB
32062  * Released under LGPL License.
32063  *
32064  * License: http://www.tinymce.com/license
32065  * Contributing: http://www.tinymce.com/contributing
32066  */
32067 
32068 /**
32069  * Creates a split button.
32070  *
32071  * @-x-less SplitButton.less
32072  * @class tinymce.ui.SplitButton
32073  * @extends tinymce.ui.Button
32074  */
32075 define("tinymce/ui/SplitButton", [
32076 	"tinymce/ui/MenuButton",
32077 	"tinymce/ui/DomUtils"
32078 ], function(MenuButton, DomUtils) {
32079 	return MenuButton.extend({
32080 		Defaults: {
32081 			classes: "widget btn splitbtn",
32082 			role: "button"
32083 		},
32084 
32085 		/**
32086 		 * Repaints the control after a layout operation.
32087 		 *
32088 		 * @method repaint
32089 		 */
32090 		repaint: function() {
32091 			var self = this, elm = self.getEl(), rect = self.layoutRect(), mainButtonElm, menuButtonElm;
32092 
32093 			self._super();
32094 
32095 			mainButtonElm = elm.firstChild;
32096 			menuButtonElm = elm.lastChild;
32097 
32098 			DomUtils.css(mainButtonElm, {
32099 				width: rect.w - DomUtils.getSize(menuButtonElm).width,
32100 				height: rect.h - 2
32101 			});
32102 
32103 			DomUtils.css(menuButtonElm, {
32104 				height: rect.h - 2
32105 			});
32106 
32107 			return self;
32108 		},
32109 
32110 		/**
32111 		 * Sets the active menu state.
32112 		 *
32113 		 * @private
32114 		 */
32115 		activeMenu: function(state) {
32116 			var self = this;
32117 
32118 			DomUtils.toggleClass(self.getEl().lastChild, self.classPrefix + 'active', state);
32119 		},
32120 
32121 		/**
32122 		 * Renders the control as a HTML string.
32123 		 *
32124 		 * @method renderHtml
32125 		 * @return {String} HTML representing the control.
32126 		 */
32127 		renderHtml: function() {
32128 			var self = this, id = self._id, prefix = self.classPrefix;
32129 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
32130 
32131 			return (
32132 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1">' +
32133 					'<button type="button" hidefocus="1" tabindex="-1">' +
32134 						(icon ? '<i class="' + icon + '"></i>' : '') +
32135 						(self._text ? (icon ? ' ' : '') + self._text : '') +
32136 					'</button>' +
32137 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
32138 						//(icon ? '<i class="' + icon + '"></i>' : '') +
32139 						(self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') +
32140 						' <i class="' + prefix + 'caret"></i>' +
32141 					'</button>' +
32142 				'</div>'
32143 			);
32144 		},
32145 
32146 		/**
32147 		 * Called after the control has been rendered.
32148 		 *
32149 		 * @method postRender
32150 		 */
32151 		postRender: function() {
32152 			var self = this, onClickHandler = self.settings.onclick;
32153 
32154 			self.on('click', function(e) {
32155 				var node = e.target;
32156 
32157 				if (e.control == this) {
32158 					// Find clicks that is on the main button
32159 					while (node) {
32160 						if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) {
32161 							e.stopImmediatePropagation();
32162 							onClickHandler.call(this, e);
32163 							return;
32164 						}
32165 
32166 						node = node.parentNode;
32167 					}
32168 				}
32169 			});
32170 
32171 			delete self.settings.onclick;
32172 
32173 			return self._super();
32174 		}
32175 	});
32176 });
32177 
32178 // Included from: js/tinymce/classes/ui/StackLayout.js
32179 
32180 /**
32181  * StackLayout.js
32182  *
32183  * Copyright, Moxiecode Systems AB
32184  * Released under LGPL License.
32185  *
32186  * License: http://www.tinymce.com/license
32187  * Contributing: http://www.tinymce.com/contributing
32188  */
32189 
32190 /**
32191  * This layout uses the browsers layout when the items are blocks.
32192  *
32193  * @-x-less StackLayout.less
32194  * @class tinymce.ui.StackLayout
32195  * @extends tinymce.ui.FlowLayout
32196  */
32197 define("tinymce/ui/StackLayout", [
32198 	"tinymce/ui/FlowLayout"
32199 ], function(FlowLayout) {
32200 	"use strict";
32201 
32202 	return FlowLayout.extend({
32203 		Defaults: {
32204 			containerClass: 'stack-layout',
32205 			controlClass: 'stack-layout-item',
32206 			endClass : 'break'
32207 		}
32208 	});
32209 });
32210 
32211 // Included from: js/tinymce/classes/ui/TabPanel.js
32212 
32213 /**
32214  * TabPanel.js
32215  *
32216  * Copyright, Moxiecode Systems AB
32217  * Released under LGPL License.
32218  *
32219  * License: http://www.tinymce.com/license
32220  * Contributing: http://www.tinymce.com/contributing
32221  */
32222 
32223 /**
32224  * Creates a tab panel control.
32225  *
32226  * @-x-less TabPanel.less
32227  * @class tinymce.ui.TabPanel
32228  * @extends tinymce.ui.Panel
32229  *
32230  * @setting {Number} activeTab Active tab index.
32231  */
32232 define("tinymce/ui/TabPanel", [
32233 	"tinymce/ui/Panel",
32234 	"tinymce/ui/DomUtils"
32235 ], function(Panel, DomUtils) {
32236 	"use strict";
32237 
32238 	return Panel.extend({
32239 		lastIdx: 0,
32240 
32241 		Defaults: {
32242 			layout: 'absolute',
32243 			defaults: {
32244 				type: 'panel'
32245 			}
32246 		},
32247 
32248 		/**
32249 		 * Activates the specified tab by index.
32250 		 *
32251 		 * @method activateTab
32252 		 * @param {Number} idx Index of the tab to activate.
32253 		 */
32254 		activateTab: function(idx) {
32255 			var activeTabElm;
32256 
32257 			if (this.activeTabId) {
32258 				activeTabElm = this.getEl(this.activeTabId);
32259 				DomUtils.removeClass(activeTabElm, this.classPrefix + 'active');
32260 				activeTabElm.setAttribute('aria-selected', "false");
32261 			}
32262 
32263 			this.activeTabId = 't' + idx;
32264 
32265 			activeTabElm = this.getEl('t' + idx);
32266 			activeTabElm.setAttribute('aria-selected', "true");
32267 			DomUtils.addClass(activeTabElm, this.classPrefix + 'active');
32268 
32269 			if (idx != this.lastIdx) {
32270 				this.items()[this.lastIdx].hide();
32271 				this.lastIdx = idx;
32272 			}
32273 
32274 			this.items()[idx].show().fire('showtab');
32275 			this.reflow();
32276 		},
32277 
32278 		/**
32279 		 * Renders the control as a HTML string.
32280 		 *
32281 		 * @method renderHtml
32282 		 * @return {String} HTML representing the control.
32283 		 */
32284 		renderHtml: function() {
32285 			var self = this, layout = self._layout, tabsHtml = '', prefix = self.classPrefix;
32286 
32287 			self.preRender();
32288 			layout.preRender(self);
32289 
32290 			self.items().each(function(ctrl, i) {
32291 				var id = self._id + '-t' + i;
32292 
32293 				ctrl.aria('role', 'tabpanel');
32294 				ctrl.aria('labelledby', id);
32295 
32296 				tabsHtml += (
32297 					'<div id="' + id + '" class="' + prefix + 'tab" ' +
32298 						'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' +
32299 						self.encode(ctrl.settings.title) +
32300 					'</div>'
32301 				);
32302 			});
32303 
32304 			return (
32305 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
32306 					'<div id="' + self._id + '-head" class="' + prefix + 'tabs" role="tablist">' +
32307 						tabsHtml +
32308 					'</div>' +
32309 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
32310 						layout.renderHtml(self) +
32311 					'</div>' +
32312 				'</div>'
32313 			);
32314 		},
32315 
32316 		/**
32317 		 * Called after the control has been rendered.
32318 		 *
32319 		 * @method postRender
32320 		 */
32321 		postRender: function() {
32322 			var self = this;
32323 
32324 			self._super();
32325 
32326 			self.settings.activeTab = self.settings.activeTab || 0;
32327 			self.activateTab(self.settings.activeTab);
32328 
32329 			this.on('click', function(e) {
32330 				var targetParent = e.target.parentNode;
32331 
32332 				if (e.target.parentNode.id == self._id + '-head') {
32333 					var i = targetParent.childNodes.length;
32334 
32335 					while (i--) {
32336 						if (targetParent.childNodes[i] == e.target) {
32337 							self.activateTab(i);
32338 						}
32339 					}
32340 				}
32341 			});
32342 		},
32343 
32344 		/**
32345 		 * Initializes the current controls layout rect.
32346 		 * This will be executed by the layout managers to determine the
32347 		 * default minWidth/minHeight etc.
32348 		 *
32349 		 * @method initLayoutRect
32350 		 * @return {Object} Layout rect instance.
32351 		 */
32352 		initLayoutRect: function() {
32353 			var self = this, rect, minW, minH;
32354 
32355 			minW = DomUtils.getSize(self.getEl('head')).width;
32356 			minW = minW < 0 ? 0 : minW;
32357 			minH = 0;
32358 			self.items().each(function(item, i) {
32359 				minW = Math.max(minW, item.layoutRect().minW);
32360 				minH = Math.max(minH, item.layoutRect().minH);
32361 				if (self.settings.activeTab != i) {
32362 					item.hide();
32363 				}
32364 			});
32365 
32366 			self.items().each(function(ctrl) {
32367 				ctrl.settings.x = 0;
32368 				ctrl.settings.y = 0;
32369 				ctrl.settings.w = minW;
32370 				ctrl.settings.h = minH;
32371 
32372 				ctrl.layoutRect({
32373 					x: 0,
32374 					y: 0,
32375 					w: minW,
32376 					h: minH
32377 				});
32378 			});
32379 
32380 			var headH = DomUtils.getSize(self.getEl('head')).height;
32381 
32382 			self.settings.minWidth = minW;
32383 			self.settings.minHeight = minH + headH;
32384 
32385 			rect = self._super();
32386 			rect.deltaH += headH;
32387 			rect.innerH = rect.h - rect.deltaH;
32388 
32389 			return rect;
32390 		}
32391 	});
32392 });
32393 
32394 // Included from: js/tinymce/classes/ui/TextBox.js
32395 
32396 /**
32397  * TextBox.js
32398  *
32399  * Copyright, Moxiecode Systems AB
32400  * Released under LGPL License.
32401  *
32402  * License: http://www.tinymce.com/license
32403  * Contributing: http://www.tinymce.com/contributing
32404  */
32405 
32406 /**
32407  * Creates a new textbox.
32408  *
32409  * @-x-less TextBox.less
32410  * @class tinymce.ui.TextBox
32411  * @extends tinymce.ui.Widget
32412  */
32413 define("tinymce/ui/TextBox", [
32414 	"tinymce/ui/Widget",
32415 	"tinymce/ui/DomUtils"
32416 ], function(Widget, DomUtils) {
32417 	"use strict";
32418 
32419 	return Widget.extend({
32420 		/**
32421 		 * Constructs a instance with the specified settings.
32422 		 *
32423 		 * @constructor
32424 		 * @param {Object} settings Name/value object with settings.
32425 		 * @setting {Boolean} multiline True if the textbox is a multiline control.
32426 		 * @setting {Number} maxLength Max length for the textbox.
32427 		 * @setting {Number} size Size of the textbox in characters.
32428 		 */
32429 		init: function(settings) {
32430 			var self = this;
32431 
32432 			self._super(settings);
32433 
32434 			self._value = settings.value || '';
32435 			self.addClass('textbox');
32436 
32437 			if (settings.multiline) {
32438 				self.addClass('multiline');
32439 			} else {
32440 				// TODO: Rework this
32441 				self.on('keydown', function(e) {
32442 					if (e.keyCode == 13) {
32443 						self.parents().reverse().each(function(ctrl) {
32444 							e.preventDefault();
32445 
32446 							if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
32447 								ctrl.fire('submit', {data: ctrl.toJSON()});
32448 								return false;
32449 							}
32450 						});
32451 					}
32452 				});
32453 			}
32454 		},
32455 
32456 		/**
32457 		 * Getter/setter function for the disabled state.
32458 		 *
32459 		 * @method value
32460 		 * @param {Boolean} [state] State to be set.
32461 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
32462 		 */
32463 		disabled: function(state) {
32464 			var self = this;
32465 
32466 			if (self._rendered && typeof(state) != 'undefined') {
32467 				self.getEl().disabled = state;
32468 			}
32469 
32470 			return self._super(state);
32471 		},
32472 
32473 		/**
32474 		 * Getter/setter function for the control value.
32475 		 *
32476 		 * @method value
32477 		 * @param {String} [value] Value to be set.
32478 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
32479 		 */
32480 		value: function(value) {
32481 			var self = this;
32482 
32483 			if (typeof(value) != "undefined") {
32484 				self._value = value;
32485 
32486 				if (self._rendered) {
32487 					self.getEl().value = value;
32488 				}
32489 
32490 				return self;
32491 			}
32492 
32493 			if (self._rendered) {
32494 				return self.getEl().value;
32495 			}
32496 
32497 			return self._value;
32498 		},
32499 
32500 		/**
32501 		 * Repaints the control after a layout operation.
32502 		 *
32503 		 * @method repaint
32504 		 */
32505 		repaint: function() {
32506 			var self = this, style, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect;
32507 
32508 			style = self.getEl().style;
32509 			rect = self._layoutRect;
32510 			lastRepaintRect = self._lastRepaintRect || {};
32511 
32512 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
32513 			var doc = document;
32514 			if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
32515 				style.lineHeight = (rect.h - borderH) + 'px';
32516 			}
32517 
32518 			borderBox = self._borderBox;
32519 			borderW = borderBox.left + borderBox.right + 8;
32520 			borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0);
32521 
32522 			if (rect.x !== lastRepaintRect.x) {
32523 				style.left = rect.x + 'px';
32524 				lastRepaintRect.x = rect.x;
32525 			}
32526 
32527 			if (rect.y !== lastRepaintRect.y) {
32528 				style.top = rect.y + 'px';
32529 				lastRepaintRect.y = rect.y;
32530 			}
32531 
32532 			if (rect.w !== lastRepaintRect.w) {
32533 				style.width = (rect.w - borderW) + 'px';
32534 				lastRepaintRect.w = rect.w;
32535 			}
32536 
32537 			if (rect.h !== lastRepaintRect.h) {
32538 				style.height = (rect.h - borderH) + 'px';
32539 				lastRepaintRect.h = rect.h;
32540 			}
32541 
32542 			self._lastRepaintRect = lastRepaintRect;
32543 			self.fire('repaint', {}, false);
32544 
32545 			return self;
32546 		},
32547 
32548 		/**
32549 		 * Renders the control as a HTML string.
32550 		 *
32551 		 * @method renderHtml
32552 		 * @return {String} HTML representing the control.
32553 		 */
32554 		renderHtml: function() {
32555 			var self = this, id = self._id, settings = self.settings, value = self.encode(self._value, false), extraAttrs = '';
32556 
32557 			if ("spellcheck" in settings) {
32558 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
32559 			}
32560 
32561 			if (settings.maxLength) {
32562 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
32563 			}
32564 
32565 			if (settings.size) {
32566 				extraAttrs += ' size="' + settings.size + '"';
32567 			}
32568 
32569 			if (settings.subtype) {
32570 				extraAttrs += ' type="' + settings.subtype + '"';
32571 			}
32572 
32573 			if (self.disabled()) {
32574 				extraAttrs += ' disabled="disabled"';
32575 			}
32576 
32577 			if (settings.multiline) {
32578 				return (
32579 					'<textarea id="' + id + '" class="' + self.classes() + '" ' +
32580 					(settings.rows ? ' rows="' + settings.rows + '"' : '') +
32581 					' hidefocus="1"' + extraAttrs + '>' + value +
32582 					'</textarea>'
32583 				);
32584 			}
32585 
32586 			return '<input id="' + id + '" class="' + self.classes() + '" value="' + value + '" hidefocus="1"' + extraAttrs + ' />';
32587 		},
32588 
32589 		/**
32590 		 * Called after the control has been rendered.
32591 		 *
32592 		 * @method postRender
32593 		 */
32594 		postRender: function() {
32595 			var self = this;
32596 
32597 			DomUtils.on(self.getEl(), 'change', function(e) {
32598 				self.fire('change', e);
32599 			});
32600 
32601 			return self._super();
32602 		},
32603 
32604 		remove: function() {
32605 			DomUtils.off(this.getEl());
32606 			this._super();
32607 		}
32608 	});
32609 });
32610 
32611 // Included from: js/tinymce/classes/ui/Throbber.js
32612 
32613 /**
32614  * Throbber.js
32615  *
32616  * Copyright, Moxiecode Systems AB
32617  * Released under LGPL License.
32618  *
32619  * License: http://www.tinymce.com/license
32620  * Contributing: http://www.tinymce.com/contributing
32621  */
32622 
32623 /**
32624  * This class enables you to display a Throbber for any element.
32625  *
32626  * @-x-less Throbber.less
32627  * @class tinymce.ui.Throbber
32628  */
32629 define("tinymce/ui/Throbber", [
32630 	"tinymce/ui/DomUtils",
32631 	"tinymce/ui/Control"
32632 ], function(DomUtils, Control) {
32633 	"use strict";
32634 
32635 	/**
32636 	 * Constructs a new throbber.
32637 	 *
32638 	 * @constructor
32639 	 * @param {Element} elm DOM Html element to display throbber in.
32640 	 * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll.
32641 	 */
32642 	return function(elm, inline) {
32643 		var self = this, state, classPrefix = Control.classPrefix;
32644 
32645 		/**
32646 		 * Shows the throbber.
32647 		 *
32648 		 * @method show
32649 		 * @param {Number} [time] Time to wait before showing.
32650 		 * @return {tinymce.ui.Throbber} Current throbber instance.
32651 		 */
32652 		self.show = function(time) {
32653 			self.hide();
32654 
32655 			state = true;
32656 
32657 			window.setTimeout(function() {
32658 				if (state) {
32659 					elm.appendChild(DomUtils.createFragment(
32660 						'<div class="' + classPrefix + 'throbber' + (inline ? ' ' + classPrefix + 'throbber-inline' : '') + '"></div>'
32661 					));
32662 				}
32663 			}, time || 0);
32664 
32665 			return self;
32666 		};
32667 
32668 		/**
32669 		 * Hides the throbber.
32670 		 *
32671 		 * @method hide
32672 		 * @return {tinymce.ui.Throbber} Current throbber instance.
32673 		 */
32674 		self.hide = function() {
32675 			var child = elm.lastChild;
32676 
32677 			if (child && child.className.indexOf('throbber') != -1) {
32678 				child.parentNode.removeChild(child);
32679 			}
32680 
32681 			state = false;
32682 
32683 			return self;
32684 		};
32685 	};
32686 });
32687 
32688 expose(["tinymce/dom/Sizzle","tinymce/html/Styles","tinymce/dom/EventUtils","tinymce/dom/TreeWalker","tinymce/util/Tools","tinymce/dom/Range","tinymce/html/Entities","tinymce/Env","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","tinymce/html/Node","tinymce/html/Schema","tinymce/html/SaxParser","tinymce/html/DomParser","tinymce/html/Writer","tinymce/html/Serializer","tinymce/dom/Serializer","tinymce/dom/TridentSelection","tinymce/util/VK","tinymce/dom/ControlSelection","tinymce/dom/Selection","tinymce/Formatter","tinymce/UndoManager","tinymce/EnterKey","tinymce/ForceBlocks","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/util/EventDispatcher","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/DomUtils","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/KeyboardNavigation","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/util/Quirks","tinymce/util/Observable","tinymce/EditorObservable","tinymce/Shortcuts","tinymce/Editor","tinymce/util/I18n","tinymce/FocusManager","tinymce/EditorManager","tinymce/LegacyInput","tinymce/util/XHR","tinymce/util/JSON","tinymce/util/JSONRequest","tinymce/util/JSONP","tinymce/util/LocalStorage","tinymce/Compat","tinymce/ui/Layout","tinymce/ui/AbsoluteLayout","tinymce/ui/Tooltip","tinymce/ui/Widget","tinymce/ui/Button","tinymce/ui/ButtonGroup","tinymce/ui/Checkbox","tinymce/ui/PanelButton","tinymce/ui/ColorButton","tinymce/ui/ComboBox","tinymce/ui/Path","tinymce/ui/ElementPath","tinymce/ui/FormItem","tinymce/ui/Form","tinymce/ui/FieldSet","tinymce/ui/FilePicker","tinymce/ui/FitLayout","tinymce/ui/FlexLayout","tinymce/ui/FlowLayout","tinymce/ui/FormatControls","tinymce/ui/GridLayout","tinymce/ui/Iframe","tinymce/ui/Label","tinymce/ui/Toolbar","tinymce/ui/MenuBar","tinymce/ui/MenuButton","tinymce/ui/ListBox","tinymce/ui/MenuItem","tinymce/ui/Menu","tinymce/ui/Radio","tinymce/ui/ResizeHandle","tinymce/ui/Spacer","tinymce/ui/SplitButton","tinymce/ui/StackLayout","tinymce/ui/TabPanel","tinymce/ui/TextBox","tinymce/ui/Throbber"]);
32689 })(this);